Commit Graph

6 Commits (37e191424674ed63f151e795ac0ac3b55ebddf4d)

Author SHA1 Message Date
nexxo 37e1914246 Merge branch 'claude/nice-moser-521659'
# Conflicts:
#	VERSION
#	deploy/install-agent.sh
#	deploy/update.sh
2026-08-04 10:31:59 +02:00
nexxo 4debc79883 Eine hängende Sperre ist jetzt ein Knopf, kein SSH-Zugang
Am 4. August 2026 hielt ein einziger Aufruf — `timeout 45 docker compose
exec` ohne `--kill-after` — die Sperre stundenlang, und jeder folgende
Lauf wurde übersprungen. Behoben ist die Ursache in v1.6.5; was fehlte,
war der Griff für das nächste Mal. Er lag per SSH auf dem Wirt.

Die Konsole kann ihn nicht selbst führen: sie ist www-data in einem
Behälter, der Sperrenhalter ist ein Prozess auf dem WIRT. Also bittet
sie den Agenten, und der ruft den root-eigenen Helfer — dasselbe Muster
wie `apply-proxy-hosts`, mit eigener sudoers-Zeile.

Die Bitte liegt in einer EIGENEN Datei, und das ist keine
Geschmacksfrage: der Agent nimmt die Sperre in seinen ersten Zeilen,
lange bevor er den Postkasten ansieht. Ein blockierter Lauf steigt
vorher aus — eine Entsperr-Bitte im Postkasten erreichte ihn also genau
dann nie, wenn sie gebraucht wird. Dazu nimmt der Postkasten eine Bitte
zur Zeit an, und die dreißig Minuten, in denen dort eine wartende
Update-Anfrage liegt, sind die, in denen jemand entsperren will.

Der Dienstbenutzer sagt „gib die Sperre frei", nicht „töte 1234": welcher
Prozess das ist, sucht der Helfer selbst. Dürfte der Anrufer die Nummer
liefern, wäre die Freigabe das Recht, jeden beliebigen Prozess als root
zu beenden.

Beendet werden alle, die die Sperrdatei OFFEN halten — nicht nur der,
der das flock genommen hat. Ein flock hängt an der offenen
Dateibeschreibung, und die wird vererbt: stirbt der Agent, sein
hängendes `docker compose exec` aber nicht, bleibt die Sperre gehalten.
Genau das war der Vorfall. Verschont bleiben PID 1 und der Anrufer samt
Vorfahren — im Betrieb hält der blockierte Agent die Datei selbst offen
und ist zugleich der, der anruft.

Erst SIGTERM, fünf Sekunden, dann SIGKILL. Eine Frist ohne Nachdruck ist
keine Frist.

Bestätigt wird im Modal (R23), das VORHER nennt, was es beendet — PID,
Laufzeit und Kommandozeile aus dem Lebenszeichen. Eigene Berechtigung
`deployment.unblock`, nicht `site.manage`: wer aktualisieren darf, darf
damit nicht automatisch in einen laufenden Vorgang hineingreifen.

Der Helfer-Vertrag steigt auf 3, und update.sh verlangt ihn. Ohne das
wäre der Knopf auf einem Wirt, der install-agent.sh seither nicht mehr
gefahren hat, ein Knopf, der still nichts tut — und die Konsole sagt es
jetzt, statt den Betreiber dorthin zurückzuschicken, wo er ohne sie
schon war.

Geprüft wird ausgeführt, nicht begutachtet: der Helfer läuft im Test
gegen eine echte Sperrdatei mit echten Prozessen daran. Das hat gleich
einen Fehler gefunden — der Helfer erbt den Deskriptor und hielt seine
eigenen Kommandosubstitutionen für Halter, sodass die Runde nie zum Ende
gekommen wäre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 10:07:24 +02:00
nexxo 9fa45e869f Ein Terminal auf den CluPilot-Server selbst
Eigene Faehigkeit, eigener Schluessel, eigene Tuer. Die Bruecke bleibt
unveraendert: sie kennt keine Hosts, nur Tickets.

Den Weg zum Wirt traegt die root-eigene Haelfte des Updaters — sie holt
sich die oeffentliche Haelfte selbst, prueft sie hart und schreibt die
Optionsliste, die das Dienstkonto nicht bestimmen darf. Kein sudo-Weg
dorthin: einen Root-Schluessel eintraegt ein Mensch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 10:06:50 +02:00
nexxo 51a97019a8 Sperren sehen und aufheben: Portalseite fuer den Inhaber, Abschnitte in der Konsole
Aufgabe 6 des Fruehwarnsystems. Der Inhaber sieht im Portal die Sperren SEINER
Instanzen und hebt sie dort auf; der Betreiber sieht in der Konsole alle, an der
Kunden- und an der Host-Detailseite, und auf der Uebersicht steht ein Hinweis,
solange irgendwo eine Sperre aktiv ist.

Aufgehoben wird ueber ein Bestaetigungs-Modal (R23), und das Modal mutiert
nichts: es wirft ein Ereignis, das die Seite auffaengt und an ihre eigene
Methode weiterreicht. Die Berechtigungspruefung bleibt damit an der einen
Stelle, an der sie schon stand — noetig, weil ein Modal ohne die Middleware der
Seite erreichbar ist (R20). Dazu die Berechtigung `instances.manage`, nach dem
Muster der bestehenden `instances.restart`-Migration; Abrechnung und Read-only
bleiben unberuehrt.

ACHTUNG, was hier sonst noch drinsteckt und NICHT zu dieser Aufgabe gehoert:
rund 150 Zeilen zum Versandtakt — das Merkmal `RidesALane`, vierzehn Mailables
und `MailLaneRoutingTest`. Die stammen aus einer PARALLEL laufenden Sitzung an
einem anderen Feature.

Wie das hineingeriet: der Implementierer dieser Aufgabe brach vor dem Commit ab
und liess seine fertige Arbeit ungespeichert im Baum. Ich habe sie dateigenau
mit `git add <dateien>` vorgemerkt, um nichts Fremdes mitzunehmen — und dabei
uebersehen, dass `git add` nur HINZUFUEGT: die andere Sitzung hatte ihre Arbeit
bereits vorgemerkt, und `git commit` schreibt den ganzen Index, nicht nur das
zuletzt Hinzugefuegte. Richtig waere `git commit -- <dateien>` gewesen.

Nichts ist verloren, und die volle Suite ist auf diesem Stand gruen (2571).
Aber diese Botschaft soll nicht behaupten, sie beschreibe alles, was hier steht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 15:53:24 +02:00
nexxo fb1ef3fc63 Rollen: aufklappbare Karten, gruppierte Rechte, Knopf im Raster 2026-07-31 22:39:46 +02:00
nexxo 93e848d3f9 Rollen und Rechte in der Konsole zuweisbar 2026-07-31 21:46:22 +02:00