ESPO-13 legt Secrets unter /srv/espo/secrets ab (vorerst
gitea_package_token). Je Secret ein Parameter; leer oder woertlicher
Platzhalter laesst den Bestand. Erst Format und Funktion aller Werte
pruefen (Probeabruf der Registry), dann atomar schreiben - ein falscher
Token ersetzt keinen funktionierenden. Getestet: gesetzt, unveraendert,
falsches Format, abgelehnter Token.
ESPO-30 holt <extension> <version> aus der Package Registry
EspoCRM_Packages: SHA-256 gegen die Registry, Name und Version gegen das
Manifest, kein Rueckschritt, Snapshot srv-vor-update vor dem Download,
Installation per command.php als www-data, Neustart und Warten auf
healthy, Nachweis ueber --list. Danach liegt keine ZIP mehr auf der VM -
auch EspoCRMs eigene Archive in data/upload/extensions werden entfernt;
Update und Deinstallation brauchen sie nicht (Quelle 10.0.8).
ESPO-20 misst espo_extensions ("Name Version; ...", "none" wenn leer) und
beschreibt den Leseweg der Version wieder richtig. Ring 0 sperrt den
Extension-Upload in der Oberflaeche. README: Nummernkreise nachgezogen,
verrutschte Zeilen der Verzeichnistabelle zurueckgeholt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ESPO-03, ESPO-10, ESPO-12 und ESPO-20 lasen version aus
defaults/config.php, config.php und config-internal.php. EspoCRM 10 fuehrt
version aber als State-Parameter in data/state.php, und Config::load() liest
die Image-Vorgaben gar nicht. Gemeldet wurde also der Vorgabewert des Images:
nach einem gescheiterten migrate die neue Version, obwohl die Instanz auf der
alten steht.
Jetzt fragen die Skripte EspoCRM ueber "command.php version" - derselbe Wert,
gegen den auch Extensions ihre acceptableVersions pruefen. Aufruf als
www-data mit absolutem Pfad (bootstrap.php wechselt selbst ins
Installationsverzeichnis); akzeptiert wird nur eine Zeile der Form 1.2.3,
damit eine PHP-Warnung nie als Version in ein Custom Field geraet. Die
grep-Rueckfallwege auf config.php entfallen, sie lasen dieselbe falsche Quelle.
Probe gegen die Integrationsinstanz 10.0.8: Ausgabe "10.0.8".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der erste Lauf auf der Pilotkunden-VM endete Failed, sichtbar war nur
needrestarts Abschlussmeldung aus dem apt-Lauf - also die Stelle, an der die
Pakete fertig installiert waren. Zwischen apt-mark hold und docker --version
gibt das Skript nichts aus; aus dem Protokoll liess sich deshalb nicht lesen,
wo der Lauf stehen geblieben ist.
Zwei Aenderungen: Die lange Ausgabe des apt-Laufs wird aufgefangen und nur im
Fehlerfall gezeigt (letzte 30 Zeilen) - sie verdraengt sonst den Bericht am
Ende, dasselbe Problem wie der Pull-Fortschritt bei ESPO-03. Und die stumme
Strecke bekommt vier knappe Standmeldungen, sodass die letzte Zeile der
Ausgabe immer verraet, wie weit der Lauf gekommen ist.
Ausserdem tragen jetzt alle Abbrueche des Hauptskripts die Zeile
ESPO02_ERGEBNIS: FEHLER - der Script Monitor sucht danach.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gemeldet von der ersten Pilotkunden-VM: "Missing privilege separation
directory: /run/sshd". Ubuntu 24.04 startet sshd ueber ssh.socket erst bei
der ersten Verbindung; /run/sshd legt systemd dabei an (RuntimeDirectory).
Auf einer frisch installierten VM, an der bisher nur die Hyper-V-Konsole
benutzt wurde, existiert das Verzeichnis deshalb nicht - und sshd -t bricht
ab, obwohl die Konfiguration einwandfrei ist. Auf der Test-VM fiel das nicht
auf, weil dort per SSH gearbeitet wurde.
Das Skript legt /run/sshd jetzt selbst an (0755, wie die Unit), laedt sshd
nur neu, wenn er ueberhaupt als Dienst laeuft, und gibt die wirksamen Werte
aus sshd -T aus. Sind sie nicht auslesbar, ist die Haertung unbewiesen und
der Lauf ein Fehler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der late-command, der /root/ERSTINBETRIEBNAHME.txt erzeugte, ist entfernt
(11 statt 12 late-commands). Auf der VM wird nichts gelesen, und eine
mitgelieferte Anleitung altert dort: ab dem zweiten Kunden stehen auf den
Maschinen verschiedene Staende, die niemand nachzieht.
Die Handgriffe stehen jetzt im Runbook des internen Repositorys
(espo-fleet-ops/docs/runbooks/erstinbetriebnahme-kunden-vm.md); der Seed
nennt es in einer Kommentarzeile, autoinstall/README.md verweist darauf.
Dabei mitgezogen: Abschnitt "Ablageort" nannte noch die Zwischenkopie
gordon.zimm/ubuntu-autoinstall (O-26 ist seit 23.09.2026 entschieden),
und drei Links zeigten auf docs/, das es in diesem Repo nicht gibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Datei /root/ERSTINBETRIEBNAHME.txt ist das Erste, was ein Techniker nach
dem ersten Start liest. Sie nannte noch einen Trigger, den es nicht gibt, und
liess die Voraussetzungen offen.
Jetzt steht drin, was vor dem ersten Task im RMM gesetzt sein muss (Company
Unique Id, Site, Device Group, Friendly Name, espo_fqdn, espo_ring,
espo_zustand), in welcher Reihenfolge ESPO-01 bis ESPO-03 laufen, und dass das
Admin-Passwort vor DNS und Portfreigabe zu wechseln ist. Dazu der veraltete
Kommentar zum Hostnamen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Erste Freigabe aus Ring 0. Abweichung zu Ring 0: innodb-buffer-pool-size 8G
statt 4G, weil die Pilotkunden-VM 16 GiB Arbeitsspeicher hat - die Haelfte,
wie am 24.09.2026 festgelegt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Einheitlich zum Rest des Vorhabens: /srv/espo, espo-deploy, espo-fleet-ops,
ESPO-xx, espo_*-Felder. Das Praefix nennt ausserdem das Produkt statt einer
Kategorie - 'crm' steht ohnehin schon im FQDN.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Unique Id ist in den Stammdaten ein Pflichtfeld und laut Dialog eindeutig -
kurz, stabil und vom Firmennamen unabhaengig: glasfaktor statt
glasfaktor-ingenieure-gmbh. Der Task uebergibt kuenftig %companyuniqueid%.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. Gewartet wird jetzt auf ALLE vier Container, nicht nur auf espocrm.
"health: starting" im Bericht war keine Aussage. Scheitert es, nennt die
Fehlermeldung, welcher Container in welchem Zustand haengt. Gilt fuer
ESPO-03, ESPO-10 und ESPO-12.
2. ESPO-10 raeumt alte Images weg: Es bleiben genau zwei je Anwendung, das
laufende und das vorherige - fuer EspoCRM wie fuer MariaDB. Das vorherige
muss bleiben, sonst zeigt die von ESPO-12 zurueckgerollte Compose auf einen
Tag, der lokal nicht mehr liegt. Alles aeltere fuellte bisher nur das
docker-lv; Docker raeumt von sich aus nichts weg.
3. ESPO-10 zeigt einen eigenen Abschnitt "Datenbank" mit Image, Version und
Digest, analog zum Abschnitt "Instanz", sowie die Images auf der VM.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim Zusammenfuehren verbraucht LVM den Snapshot; bleibt er stehen, wurde das
Zusammenfuehren auf die naechste Aktivierung verschoben und der Stand ist noch
nicht zurueckgenommen. Das Skript sagt jetzt welches von beidem eingetreten ist
und zeigt am Ende die freien Extents - wie ESPO-11 es tut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stack anhalten, /srv aushaengen, lvconvert --merge, wieder einhaengen, starten.
Das Aushaengen ist der Kern: Ist der Ursprung geoeffnet, verschiebt LVM das
Zusammenfuehren auf die naechste Aktivierung, also auf einen Neustart - hier
geschieht es sofort und nachpruefbar.
Scheitert etwas vor dem Zusammenfuehren, wird der Dienst wieder gestartet und
nichts zurueckgenommen: eine stehende Instanz ist das schlechteste Ergebnis.
Ein ungueltiger Snapshot fuehrt zum ehrlichen Abbruch statt zu einem Versuch.
Weil ganz /srv zurueckgenommen wird, kommt auch die gerenderte Compose mit dem
alten Image-Tag zurueck - die Instanz startet damit von selbst auf dem alten
Stand, ohne Migration.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Ursache ist bekannt - die Version steht in den Vorgabewerten des Images.
Das Geruest zur Fehlersuche kann damit weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12.3 ist ein wandernder Tag und wuerde die naechste neu aufgesetzte Instanz auf
12.3.4 heben, ohne dass sich im Repository etwas aendert. 12.3.3 ist der Stand,
der auf der Test-VM laeuft und sich bewaehrt hat; ein Versionswechsel entsteht
damit nur noch durch einen Commit und den Ringweg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der vorige Commit hat nur den Kommentar geaendert, der Ersetzungsausdruck griff
nicht. Jetzt lesen alle drei Skripte defaults, config und config-internal in
dieser Reihenfolge; der spaetere Wert gewinnt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gemessen: Auf einer frisch installierten Instanz steht 'version' weder in
data/config.php noch in data/config-internal.php - EspoCRM schreibt nur, was
vom Vorgabewert abweicht. Beide Dateien luden einwandfrei, der Schluessel fehlt
schlicht.
Gelesen werden jetzt alle drei Dateien in der Reihenfolge, in der EspoCRM sie
selbst zusammenfuehrt: defaults, config, config-internal - der spaetere Wert
gewinnt. Nach einem Upgrade sticht damit data/config.php den Vorgabewert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ESPO-20 meldete 'EspoCRM-Version nicht ermittelbar'. Ursache ist vermutlich der
relative Pfad: docker exec startet im Arbeitsverzeichnis des Images, data/...
ohne fuehrenden Schraegstrich geht dann ins Leere.
Jetzt absolute Pfade, beide Konfigurationsdateien (config-internal zuerst), als
Rueckfall ein grep ueber den Bind-Mount. Bleibt es leer, zeigt ESPO-20 welche
Dateien es gibt und welche Schluessel sie tragen - dann ist die Ursache nach
einem Lauf bekannt statt nach dreien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine Messung fuer alle Werte - sie stammen aus demselben Augenblick, die teuren
Aufrufe passieren einmal, und die Wertzeilen im Task sind nur noch ein cat.
Laesst sich ein Wert nicht ermitteln, bricht der Schritt ab; die Folgezeilen
laufen dann nicht und kein Feld wird ueberschrieben. Eine stehende Instanz darf
die Werte nicht leeren, sonst sieht eine Stoerung aus wie eine leere Instanz.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Aus ESPO-04 und ESPO-05 werden ESPO-10 und ESPO-11. Ein Zehnerkreis
kennzeichnet den Block und beginnt bei x0; 0x bleibt der Weg von der leeren VM
zur laufenden Instanz, 1x das Aktualisieren, 2x Betrieb und Ueberwachung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ESPO-04 legt zuerst den LVM-Snapshot an und bricht ab, wenn das nicht gelingt -
kein Update ohne Rueckweg. Es bricht ausserdem ab, wenn noch ein Snapshot aus
einem frueheren Lauf besteht: Das hiesse, dass das vorige Update nie bestaetigt
wurde, und zwei Snapshots uebereinander waeren kein Rueckweg mehr.
ESPO-05 verwirft den Snapshot, nachdem jemand die Instanz geprueft hat, und
haelt vorher Fuellgrad und Gueltigkeit fest - war er bereits vollgelaufen, hat
der Rueckweg schon vorher nicht mehr getragen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/srv bekommt den gesamten Rest minus 10 GiB. Ein LVM-Snapshot haelt nur die
waehrend seiner Lebensdauer geaenderten Bloecke - 10 GiB reichen dafuer
reichlich, eine Reserve in Prozent war zu grob gedacht. Das lvreduce laeuft vor
dem mkfs und ist deshalb risikolos.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die VHDX ist dynamisch und jederzeit erweiterbar; ungenutzte Extents sparen auf
dem Host nichts. Aus 80%FREE wird 100%FREE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Grundeinstellungen gehoeren zum Stand eines Rings wie die Compose: Eine
Aenderung daran soll denselben Freigabeweg nehmen. Die Datei liegt jetzt in
allen drei Ringordnern, die gemeinsame Fassung ist entfallen.
ESPO-03 holt sie aus dem Ordner des jeweiligen Rings; die Zuordnung Ring zu
Ordner steht damit an zwei Stellen und ist in beiden vermerkt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Abnahmelauf meldete 'Version: unbekannt' - das grep auf data/config.php
griff nicht. Jetzt fragt das Skript EspoCRM im Container (config.php und
config-internal.php zusammengefuehrt) und faellt nur ersatzweise auf grep
ueber beide Dateien zurueck.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der erste Lauf hat die Instanz erfolgreich gestartet (db und espocrm healthy,
daemon und websocket gestartet), der Task galt aber als Failed: Die tausenden
Fortschrittszeilen des Image-Pulls haben den Abschlussbericht verdraengt.
up -d laeuft jetzt mit --quiet-pull, die Ausgabe wird aufgefangen und nur bei
Fehlschlag mit den letzten 20 Zeilen gezeigt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fester Wert statt gerechnetem Platzhalter, vorerst 4G. Als command-Eintrag,
nicht als eigene Konfigurationsdatei: Der Entrypoint stellt mariadbd davor und
prueft die Option beim Start, ein Tippfehler faellt also sofort auf. Ohne die
Zeile waeren es 128 MB.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Pruefung auf vorhandene Container, docker-compose.yml und data/config.php
entfaellt. Dass der Lauf nur an einer neuen Instanz stattfindet, stellt der Task
ueber If/Then auf espo_zustand sicher.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der haengende ScreenConnect-Client wird ueber die Default-Policies geloest, damit
er gar nicht erst installiert wird - nicht im Skript. ESPO-01 ist damit wieder
auf seine neun Schritte zurueck, ESPO-02 auf die knappe Fehlerzeile.
Aus der offiziellen Docker-Doku bleibt, was dorthin gehoert: das Entfernen
konkurrierender Pakete vor der Installation und ca-certificates/curl vor dem
Schluessel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Auf der Test-VM liegt der ScreenConnect-Client des Agenten im Zustand iU
(entpackt, nie konfiguriert), weil ihm java5-runtime fehlt. In diesem Zustand
blockiert er jede apt-Operation; ESPO-02 scheiterte daran, nicht an den
Docker-Pins.
ESPO-01 entfernt den Client jetzt als erstes (Paketname traegt je Geraet ein
anderes Suffix, daher Mustersuche), raeumt mit --fix-broken auf und bricht ab,
wenn apt danach immer noch gebrochene Abhaengigkeiten meldet. Damit ist O-20
im Skript erledigt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei apt-Fehler ohne eine einzige Zeile aus dem Skript liessen offen, ob
ueberhaupt die Repo-Fassung gelaufen ist. Die Standzeile beantwortet das
kuenftig in der ersten Ausgabezeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Abgleich mit docs.docker.com/engine/install/ubuntu zeigte eine fehlende
Vorstufe: Die Doku verlangt, docker.io, docker-compose, docker-doc,
docker-buildx, podman-docker sowie containerd und runc zu entfernen, bevor
installiert wird - containerd.io buendelt containerd und runc und steht mit
beiden in Conflicts. Genau das passt zum Symptom "unerfuellte Abhaengigkeiten".
Ausserdem wie in der Doku ca-certificates und curl vor dem Schluessel. Die
deb822-Quelle entspricht bereits woertlich der Doku; beim Pinning bleiben wir
strenger als sie (sie pinnt nur docker-ce und docker-ce-cli).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die vier gepinnten Versionen existieren im Docker-Repo fuer noble (geprueft
gegen den Packages-Index), die Ursache liegt also woanders. Statt zu raten
gibt das Skript jetzt im Fehlerfall die Simulation von apt (mit der konkreten
unerfuellten Abhaengigkeit), die verfuegbaren Versionen inklusive iptables und
nftables, die bereits installierten Docker-Pakete und haengende Installationen
aus. Ausserdem -q statt -qq, damit apt ueberhaupt etwas sagt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Task entscheidet per If/Then auf espo_zustand, ob er laufen darf; die
Skripte brauchen den Wert dann nicht mehr. ESPO-01 nimmt nur noch den
Kundennamen, ESPO-02 kein Argument, ESPO-03 fqdn und ring.
In ESPO-03 bleibt die Pruefung auf eine vorhandene Instanz - sie beruht auf
Tatsachen auf der VM, nicht auf einem Feldwert, und verhindert eine Migration
ohne geprueftes Backup.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Statt lokaler Merkmale steuert das Custom Field espo_zustand zentral, was laufen
darf. Jedes der drei Skripte nimmt den Wert als Argument und bricht ohne
Aenderung ab, wenn er nicht NEU ist.
In ESPO-03 bleibt die Pruefung auf eine vorhandene Instanz als Rueckfall
erhalten: Der Zustand im RMM ist von Hand aenderbar, eine laufende Instanz ist
es nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Richtet EspoCRM auf einer vorbereiteten VM zum ersten Mal ein: Verzeichnisse,
Geheimnisse auf der VM erzeugt, config-override.php bereitgelegt, Compose aus
dem Stand des Rings gerendert, up -d, Warten auf app-check, Abschlusspruefung.
Bricht ab, sobald eine Instanz vorhanden ist (Container, Compose-Datei oder
data/config.php). Damit kann dieses Skript keine Migration ohne geprueftes
Backup ausloesen - Aktualisierungen bekommen einen eigenen Weg.
Die Abschlusspruefung misst zugleich die in ADR 0008 offenen Punkte:
Zeichensatz des Servers und Laenge der Record-IDs (36 = UUIDv4 aktiv, sonst
Abbruch). Die Datenbankabfragen laufen im Container und lesen das Passwort dort
aus der Secret-Datei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Statt einer Vorlage fuer alle liegt je Ring eine vollstaendige Compose im Repo.
Eine Freigabe ist damit das Kopieren der Datei in den naechsten Ordner und in
jedem git diff sichtbar. Fuer den Anfang ist nur "0 - intern" gefuellt; die
beiden anderen Ordner erklaeren, dass dort noch kein Stand freigegeben ist.
espo-compose-rendern nimmt jetzt <fqdn> und <ring> und bricht ab, wenn fuer den
Ring kein Stand hinterlegt ist. Die Ringwerte 0/1/2 entsprechen dem Custom Field
espo_ring.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Vorlage ist fuer alle Kunden identisch und traegt genau einen Platzhalter
(__FQDN__); bin/espo-compose-rendern setzt ihn ein und schreibt
/srv/espo/docker-compose.yml. Keine .env mehr: alles ausser den Geheimnissen
steht in der Compose selbst.
Inhalt der Vorlage: Bind-Mounts unter /srv/espo, Datei-Secrets fuer die beiden
MariaDB-Passwoerter, Record-IDs als uuid4/36 ueber configs (in allen drei
EspoCRM-Diensten, weil configs nicht ueber volumes_from vererbt werden),
adminUpgradeDisabled, WebSocket ueber wss://<fqdn>/wss, Healthchecks mit
Startfenstern fuer den Migrationslauf.
ESPO-02 setzt keine abweichende Bindeadresse mehr - die Ports muessen fuer den
Reverse Proxy erreichbar sein. Stattdessen beschraenkt eine DOCKER-USER-Regel
den Zugang auf das eigene Subnetz, das die VM selbst ermittelt; eine
systemd-Unit setzt sie nach jedem Docker-Start neu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>