Files
dogfather-universe/DEPLOY.md
T
DogFatherGitandClaude Opus 5 603319a145 Arbeitsschloss: zwei Sitzungen sehen sich jetzt
HEUTE ZWEIMAL NUR GUTGEGANGEN. Zwei Claude-Sitzungen arbeiteten
gleichzeitig in diesem Verzeichnis, ohne voneinander zu wissen.
Keine hat etwas falsch gemacht — sie konnten es nicht wissen.

  * Beide haben `git add -A` benutzt. Haette die eine
    unfestgeschriebene Arbeit der anderen im Baum gehabt, waere sie
    mitcommittet worden — unter fremdem Namen, in einer fremden
    Begruendung, und niemandem waere es aufgefallen. Nachgesehen:
    diesmal war nichts dabei.
  * Beide haben den Stempellauf gestartet. Der schreibt 45 Dateien
    um. Wer dort eine offen hatte, bekam sie unter den Haenden weg
    geaendert.

Dasselbe hat in RunOne am 03.09.2026 sieben Minuten Ausfall
gekostet. Dort gibt es seitdem `arbeitsschloss.sh`; diese Fassung
uebernimmt seine Lehren.

WAS ES IST UND WAS NICHT. Es ist kein Riegel — wer wirklich muss,
kommt vorbei. Es beantwortet die eine Frage, die heute niemand
beantworten konnte: „arbeitet hier gerade sonst jemand?"

Die teuren Fehler liegen bei einem Schloss alle in derselben
Richtung: Es blockiert zu viel und wird deshalb abgeschafft. Also:

  * Es blockiert NICHT, wenn das Schloss DEINES ist. RunOnes erste
    Fassung fragte „ist abgeschlossen" statt „haelt es jemand
    anders" — damit haette, wer ordentlich abschliesst, nie mehr
    ausliefern koennen. Dafuer gibt es `fremd`.
  * Es VERFAELLT nach zwei Stunden, und dass da jemand war, steht
    beim Uebernehmen dabei.
  * Es blockiert NICHT, wenn es selbst unlesbar ist — dritter
    Ausgang, kein Stillstand.
  * Notausgang: SCHLOSS_ZWANG=ja git commit …

DAS PROBLEM, AN DEM RUNONE HAENGT, IST HIER GELOEST. Dort faellt die
Kennung im Zweifel auf den Systembenutzer zurueck, und zwei
Claudian-Sitzungen laufen BEIDE als `claudian` — die Sicherung griff
ausgerechnet zwischen den zwei Faellen nicht, fuer die sie gebaut
wurde. Deshalb ist dort `export ARBEITER=…` Pflicht, und Pflicht
heisst: man vergisst es.

Hier steht `CLAUDE_CODE_SESSION_ID` in jeder Sitzung und ist je
Sitzung verschieden (nachgesehen, 36 Zeichen UUID). Zwei Sitzungen
auf demselben Windows-Benutzer unterscheiden sich damit von selbst,
ohne dass jemand etwas tun muss. Reihenfolge: ARBEITER, dann
Sitzungskennung, dann Benutzername MIT Warnung.

NIEMAND MUSS DARAN DENKEN:
  * die beiden Stempelwerkzeuge nehmen es selbst und geben es selbst
    frei — auch nach einem Absturz und bei Strg+C (wie `bauen.sh` im
    Shop; eines, an das man denken muss, wird vergessen und ab da
    umgangen)
  * `tools/git-haken/pre-commit` bricht jeden Commit ab, solange
    jemand ANDERS das Schloss haelt. Das ist die Stelle, die heute
    gefehlt hat: `git add -A` ist der Griff, den man ohne Nachdenken
    macht, und gegen einen Reflex hilft keine Regel auf Papier.
  * `core.hooksPath` statt `.git/hooks` — letzteres ist nicht
    versioniert und waere nach einem Klon genau dann leer, wenn es
    gebraucht wird.

GEPRUEFT, server/pruef-arbeitsschloss.mjs: 33 Pruefungen, 0 Fehler —
in einem WEGWERF-Verzeichnis mit eigenem git, damit kein echtes
Schloss angefasst wird. Darunter am echten git:

    ohne Schloss committen        -> geht      (0)
    mit dem EIGENEN Schloss       -> geht      (0)
    mit einem FREMDEN Schloss     -> bricht ab (1), nennt wer und warum
    und es stehen genau ZWEI Commits da, nicht drei
    SCHLOSS_ZWANG=ja              -> kommt vorbei
    ohne das Schloss-Werkzeug     -> laesst durch

Dazu: verfallenes Schloss laesst durch, mit laengerer Frist blockt
dasselbe Schloss wieder (Gegenprobe), unlesbarer Inhalt und
unlesbarer Zeitstempel blockieren nicht.

Portnummern nach der neuen Pruefdatei nachgemessen: Pruefbereich bis
5415, 462 Nummern, 0 Kollisionen. pruef-struktur 75/0,
pruef-fingermass 5/0, pruef-ports 10/0.

In DEPLOY.md steht es jetzt an erster Stelle — eine Sicherung, von
der nur der weiss, der sie gebaut hat, ist die erste, die umgangen
wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-01 20:04:28 +02:00

22 KiB
Raw Blame History

DOGFATHER UNIVERSE — Go-Live-Anleitung

⚠️ ARBEITET HIER GERADE SONST JEMAND? (seit 01.10.2026)

An diesem Tag arbeiteten zwei Claude-Sitzungen gleichzeitig in diesem Verzeichnis, ohne voneinander zu wissen. Keine hat etwas falsch gemacht — sie konnten es nicht wissen. Zweimal ist es nur gut gegangen:

  • Beide haben git add -A benutzt. Hätte die eine unfestgeschriebene Arbeit der anderen im Baum gehabt, wäre sie mitcommittet worden — unter fremdem Namen, in einer fremden Begründung, und niemandem wäre es aufgefallen.
  • Beide haben tools/workspace-stempel.mjs laufen lassen. Der schreibt 45 Dateien um. Wer dort eine offen hatte, bekam sie unter den Händen weg geändert.

Dasselbe hat in RunOne am 03.09.2026 sieben Minuten Ausfall gekostet.

Vor dem Arbeiten:

node tools/arbeitsschloss.mjs ansehen        # arbeitet hier jemand?
node tools/arbeitsschloss.mjs nehmen "was ich tue"
node tools/arbeitsschloss.mjs freigeben      # am Ende

Du musst nichts von Hand tun, wenn du nur stempelst oder committest — die beiden Stempelwerkzeuge nehmen das Schloss selbst, und ein git-Haken (tools/git-haken/pre-commit) bricht jeden Commit ab, solange jemand anders das Schloss hält.

Was es NICHT tut — das ist der wichtigere Teil:

  • Es blockiert nicht, wenn das Schloss deines ist. Wer ordentlich abschließt, soll nicht bestraft werden.
  • Es verfällt nach zwei Stunden. Ein Schloss, das man vergessen kann, blockiert sonst dauerhaft — und wird beim ersten Ärger umgangen. Ab da ist es wertlos.
  • Es blockiert nicht, wenn es selbst kaputt oder nicht lesbar ist.
  • Notausgang, falls du trotzdem musst: SCHLOSS_ZWANG=ja git commit …

Nach einem frischen Klon einmal: node tools/arbeitsschloss.mjs einrichten (setzt core.hooksPath; .git/hooks wird nicht versioniert und wäre sonst leer). node server/pruef-arbeitsschloss.mjs sagt dir, ob alles sitzt — 33 Prüfungen, darunter ein echter Commit gegen ein fremdes Schloss.

Und bei zwei Claude-Sitzungen: Das Schloss macht die Gleichzeitigkeit sichtbar, es ersetzt das Reden nicht. SendMessage an die andere Sitzung kostet zehn Sekunden.


⚠️ ZUERST LESEN: Die echte Seite läuft auf dem NETCUP-SERVER, nicht auf Cloudflare

Seit dem öffentlichen Start (21.08.2026) wird dogfather-universe.com vom eigenen Netcup-Server ausgeliefert. Der alte Cloudflare-Worker existiert zwar noch und lässt sich auch weiterhin deployen — er erreicht die echte Domain aber nicht mehr.

Das ist die gefährlichste Stelle im ganzen Projekt: npx wrangler deploy läuft ohne Fehler durch und meldet „Deployed", die Änderung ist danach auf …workers.dev sichtbar — und auf dogfather-universe.com passiert nichts. Genau so ist es am 22.08.2026 beim Sprachfenster- Fix passiert (siehe unten). Wer nur die Erfolgsmeldung von Wrangler liest, meldet „ist live", obwohl Filipe auf dem Handy weiterhin den kaputten Stand sieht.

Adresse Läuft wo Wird von wo beliefert
dogfather-universe.com ← die echte Seite Netcup, Caddy → localhost:4100, systemd-Dienst dogiweb.service, Verzeichnis /home/dogiweb/dogfather-universe/ Gitea git.dogfather-universe.com/DogFatherGit/dogfather-universe, Zweig main
www.dogfather-universe.com dasselbe (Caddy nimmt beide Namen) dasselbe
dogfather-universe.dogfather1608.workers.dev Cloudflare Worker (Altbestand) npx wrangler deploy

Erkennungsmerkmal im Zweifel: curl -sI https://dogfather-universe.com/ | grep -i via → zeigt via: 1.1 Caddy, also Netcup. Käme die Seite von Cloudflare, stünde da kein Caddy.

So wird eine Änderung wirklich live (der einzige gültige Weg)

cd ~/Documents/Obelix/DogiHompage
# 0. STEMPELN — sonst kommt die Änderung bei niemandem an (siehe unten)
node tools/workspace-stempel.mjs   # wenn workspace/ angefasst wurde
node tools/seiten-stempel.mjs      # wenn assets/ angefasst wurde
# 1. Änderung committen
git add <dateien> && git commit
# 2. In die Ablage schieben (Gitea ist die Quelle der Wahrheit)
git push gitea master:main
# 3. Auf dem Server holen
ssh dogfather-server "cd /home/dogiweb/dogfather-universe && git pull --ff-only origin main"

⚠️ Schritt 0 ist kein Beiwerk

Der Server schickt zu Stilvorlagen, Skripten und Bildern:

Cache-Control: public, max-age=31536000, immutable

immutable heißt: Der Browser fragt nicht einmal nach. Wer die Seite einmal geladen hat, behält diese Dateien bis zu einem Jahr — oder bis sich ihre Adresse ändert. Genau dafür hängt der Stempel (?v=…) daran.

Das ist schon passiert: Auf der öffentlichen Website stand der Stempel vom 27.08.2026, während sechs Commits assets/ geändert hatten — darunter der Partnercode DOGI10 und der komplette Sprachumbau. Fünf Wochen lang kam keine dieser Änderungen bei einem wiederkehrenden Besucher an. Sie lagen auf dem Server, sie waren ausgeliefert, und niemand sah sie.

pruef-zwischenspeicher prüft seit dem 30.09.2026, dass der Stempel nicht älter ist als die Dateien, auf die er zeigt — nicht bloß, dass einer dasteht.

Statische Dateien (HTML/CSS/JS/Bilder) sind damit sofort live — der Express-Dienst liefert das Verzeichnis direkt aus, ein Neustart ist dafür nicht nötig.

⚠️ ES GIBT ZWEI CHECKOUTS AUF DEM SERVER, NICHT EINEN

Am 22.08.2026 beim Ausrollen des Webdesign-Bereichs gefunden — diese Datei beschrieb vorher nur den ersten und war damit unvollständig:

Dienst Checkout Liefert
dogiweb.service (Port 4100) /home/dogiweb/dogfather-universe/ die öffentliche Website + server/
dogiintern.service (Port 4200) /home/dogiintern/dogfather-universe/ die API unter postfach.dogfather-universe.com + server-internal/

Ein git pull in /home/dogiweb ändert an server-internal/ also gar nichts. Wer nur dort zieht und danach dogiintern.service neu startet, startet den Dienst mit unverändertem Code neu und wundert sich, warum nichts passiert.

claudian hat auf /home/dogiintern/ keinen Zugriff (weder lesend noch über den begrenzten sudo). Änderungen an server-internal/ muss deshalb Filipe selbst ausrollen.

Der Standard-Fall (nur Code geändert, keine neuen/geänderten Pakete):

sudo -u dogiintern git -C /home/dogiintern/dogfather-universe pull --ff-only origin main
sudo systemctl restart dogiintern.service

Der volle Fall (auch package.json/package-lock.json geändert — dann MUSS npm ci mit, sonst laufen Code und Pakete auseinander). Ein Block, bricht bei jedem Fehler ab, und startet den Dienst erst neu, wenn Pull, Installation und ein Lade-Test der nativen Module (better-sqlite3) durch sind — schlägt etwas fehl, läuft der alte Dienst unberührt weiter:

sudo -u dogiintern git -C /home/dogiintern/dogfather-universe pull --ff-only origin main && \
sudo -u dogiintern bash -c 'cd /home/dogiintern/dogfather-universe/server-internal && npm ci' && \
sudo -u dogiintern node -e "require('/home/dogiintern/dogfather-universe/server-internal/node_modules/better-sqlite3'); console.log('native Module laden: ok')" && \
sudo systemctl restart dogiintern.service && \
sleep 6 && echo "Dienst:" $(systemctl is-active dogiintern.service) && \
curl -s -o /dev/null -w "health nach Neustart: %{http_code}\n" https://postfach.dogfather-universe.com/health

Wichtig — kein führendes cd. /home/dogiintern/ steht auf 700; selbst dogi darf per cd nicht hinein, nur der Benutzer dogiintern über sudo -u. Deshalb git -C <pfad> (braucht kein cd) und das npm ci in einer sudo -u dogiintern bash -c 'cd … && npm ci'-Shell, die den Wechsel als dogiintern ausführt. Ein cd als erste Zeile scheitert an „Keine Berechtigung".

Zwei Warnungen von npm ci, die HARMLOS sind (am 28.08.2026 verifiziert):

  1. npm warn allow-scripts … better-sqlite3 … (install: prebuild-install || node-gyp rebuild) — better-sqlite3 baut sich normalerweise über ein Install-Skript. Die allowScripts-Sperre auf dem Konto blockiert das, aber prebuild-install lädt ein fertig kompiliertes Binary, das ohne den Build-Schritt funktioniert. Kontrolle: Läuft der Dienst danach (active) und steht in den Logs „… Einstellungen aus der Datenbank geladen", ist better-sqlite3 in Ordnung. Der native Module laden: ok-Schritt im Block prüft genau das vorab.
  2. npm warn deprecated [email protected] — nur ein Hinweis, kein Fehler.

sleep 6, nicht 2. Der Dienst braucht nach dem Neustart ein paar Sekunden, bis er auf Port 4200 hört. Ein zu früher health-Check meldet sonst kurzzeitig 502 (Caddy erreicht den Dienst noch nicht) und Dienst: activating, obwohl gleich darauf alles läuft. Erst bei anhaltendem 502/activating ist wirklich etwas kaputt — dann sudo systemctl status dogiintern.service --no-pager ansehen.

Erwartete letzte Zeilen: native Module laden: ok, Dienst: active, health nach Neustart: 200. Kommt stattdessen ein Abbruch VOR dem Neustart, ist nichts passiert — der alte Dienst läuft weiter, und der Fehler (meist ein fehlgeschlagener npm ci) lässt sich in Ruhe ansehen.

Server-Code geändert? Dann ist ein Neustart PFLICHT

Statische Dateien sind nach dem Pull sofort live. Server-Code nicht — der läuft weiter mit dem alten Stand, bis der Dienst neu startet.

ssh dogfather-server "sudo systemctl restart dogiweb.service"     # nach Änderungen in server/
ssh dogfather-server "sudo systemctl restart dogiintern.service"  # nach Änderungen in server-internal/

Warum das hier besonders steht (Vorfall 22.08.2026): Nach dem Pull des Webdesign-Bereichs war /webdesign/ für einige Minuten ohne Zugangsschutz öffentlich erreichbar (HTTP 200 statt der Umleitung zur Zugangswand). Die HTML-Dateien waren durch den Pull sofort da — die Schranke in server/webdesign-gate.js lief aber erst nach dem Neustart von dogiweb.service. Bei einem Bereich, der ausdrücklich nicht öffentlich sein soll, ist genau dieses Zeitfenster der gefährliche Teil eines Deploys.

Merksatz: Erst neu starten, dann „ist live" melden — und danach mit curl -I prüfen, dass die Schranke wirklich greift:

curl -sI https://dogfather-universe.com/webdesign/ | grep -iE "^location|x-robots-tag"
# erwartet: location: /webdesign/zugang.html?next=...   und   x-robots-tag: noindex, ...

Achtung bei Zweig-Namen: lokal heißt der Zweig master, auf dem Server und in Gitea main. Deshalb master:main beim Push. Ein blankes git push schiebt sonst nach gitea/master — einen alten, abgehängten Zweig, den niemand ausliefert.

Abhängigkeiten: npm ci, nicht npm install

Seit dem 26.08.2026 ist package-lock.json versioniert. Auf dem Server gilt deshalb:

npm ci        # richtig: installiert exakt das, was in der Lock-Datei steht
npm install   # falsch: darf neuere Fassungen ziehen und schreibt die Datei um

Warum der Unterschied zählt. In package.json steht "express": "^4.21.2" — das erlaubt alles unter 5.0. Vor dieser Umstellung lief auf dem Server deshalb express 4.22.2, während hier 4.21.2 stand. Geprüft wurde also nie ganz das, was ausgeliefert wurde. Solche Unterschiede fallen nicht beim Deploy auf, sondern im Betrieb — und dann sucht man den Fehler im eigenen Code.

npm ci löscht node_modules vorher vollständig und baut streng nach der Lock-Datei neu auf. Es schreibt sie nie um; passt sie nicht zur package.json, bricht es ab, statt still etwas anderes zu installieren.

Die Lock-Datei war früher ausgeschlossen (.gitignore), weil ein git pull daran scheiterte: Git überschreibt keine unverfolgte Datei, auch wenn ihr Inhalt derselbe ist. Das war ein formaler Konflikt, kein inhaltlicher — nachgemessen waren beide Fassungen Byte für Byte identisch. Wenn dieser Fall irgendwo erneut auftritt, ist die Lösung, die Datei auf dem Server einmal zu entfernen und danach aus dem Repo zu holen:

ssh dogfather-server "rm -f /home/dogiweb/dogfather-universe/server/package-lock.json"
ssh dogfather-server "cd /home/dogiweb/dogfather-universe && git pull --ff-only origin main"

Der Webdesign-Bereich: zwei Zahlen — seit 30.09.2026 automatisch

Die App speichert Dateien zwischen. Nach einer Änderung an webdesign/ oder assets/ müssen beide Stellen hoch, sonst bekommen Geräte, die die Seite schon einmal geöffnet haben, weiterhin den alten Stand:

webdesign/sw.js       const CACHE_NAME = "dogfather-webdesign-<Stempel>";
assets/js/wd-core.js  .register("/webdesign/sw.js?v=<Stempel>", …)

Das macht node tools/seiten-stempel.mjs jetzt mit — dieselbe Zahl wie die Seiten. Von Hand muss hier nichts mehr gezählt werden.

Warum das nötig wurde. Dieser Abschnitt stand seit dem 26.08.2026 hier, mit Begründung und Messwerten. Gemessen am 30.09.2026 standen beide Zahlen seit dem 27.08. auf v64, während sieben Commits die Dateien geändert hatten, die der Service Worker vorhält — darunter /assets/css/main.css. Ein Kommentar, der vor einem Fehler warnt, verhindert ihn nicht. pruef-zwischenspeicher prüft jetzt, dass beide Zahlen gleich und nicht älter als die vorgehaltenen Dateien sind.

Warum zwei und nicht eine. CACHE_NAME wirft den Zwischenspeicher weg, sobald der Service Worker startet. Die Nummer in der Adresse sorgt dafür, dass er überhaupt neu geladen wird — und das ist hier nicht selbstverständlich:

Gemessen am 26.08.2026: Der Server liefert sw.js mit Cache-Control: no-cache aus. Cloudflare ersetzt das durch max-age=14400 — vier Stunden, auch bei cf-cache-status: MISS. Ursache ist eine feste „Browser Cache TTL" in den Cloudflare-Einstellungen. Ohne die Nummer in der Adresse erreicht jede Änderung am Service Worker die Geräte also bis zu vier Stunden zu spät. Solange alles läuft, fällt das nie auf; ist der Stand fehlerhaft, sind es vier Stunden ohne Reparaturmöglichkeit.

Was auf dem Server automatisch läuft

Beides über /etc/cron.d/, beides als root. Die Skripte liegen im Repo und werden über den normalen git pull aktualisiert — ein eigener Deploy-Schritt ist nicht nötig.

Wann Was Skript
täglich 03:15 Sicherung von Datenbank und Uploads server-internal/sicherung.sh
alle 5 Minuten Wächter über 19 Punkte (inkl. „läuft die Sicherung?") server-internal/waechter.mjs

Sicherung. Nutzt SQLites eigenen .backup-Befehl, keine Dateikopie — die Datenbank ist 778 KB groß, ihr WAL 4,1 MB, eine Kopie der .db allein wäre also weitgehend leer. 14 tägliche Stände, sonntags zusätzlich ein Wochenstand (8 davon). Jeder Stand wird sofort nach dem Anlegen geprüft. Wiederherstellung: server-internal/wiederherstellen.sh, zeigt ohne Argument die verfügbaren Stände.

Wächter. Prüft sechs Dienste, neun Adressen, Plattenplatz und zwei Zertifikatslaufzeiten. Meldet per Push, und zwar nur bei Zustandswechsel — nicht alle fünf Minuten dasselbe. Läuft als root und verschickt selbst; ein Wächter, der über den internen Dienst meldet, wäre ausgerechnet dann still, wenn dieser das Problem ist.

Meldeweg prüfen, ohne auf eine Störung zu warten:

sudo /usr/local/bin/node /home/dogiweb/dogfather-universe/server-internal/waechter.mjs --probe

⚠️ Grenze: Ist der Server als Ganzes weg — Netz, Strom, Hardware —, meldet auch der Wächter nichts. Dagegen hilft nur eine Überwachung außerhalb der Maschine.

Die Verwaltung ist eine eigene App

Seit dem 26.08.2026 hat webdesign/verwaltung.html ein eigenes Manifest (verwaltung.webmanifest) mit eigener id und eigenem Symbol. Ohne die unterschiedliche id hielten Browser beide für dieselbe App, und die zweite Installation überschriebe die erste.

Das Manifest steht in der Ausnahmeliste von server/webdesign-gate.js — ohne diesen Eintrag antwortet es mit 302 auf die Zugangswand, und der Browser bietet „App installieren" gar nicht erst an.

Pflicht-Prüfung nach JEDEM Deploy

Nicht auf die Erfolgsmeldung des Deploy-Befehls verlassen, sondern die echte Domain fragen:

# Kommt meine Änderung wirklich auf der Seite an, die Filipe benutzt?
curl -s https://dogfather-universe.com/assets/css/main.css | grep "<mein Merkmal>"

Erst wenn das anschlägt, gilt eine Änderung als erledigt. Bei optischen Änderungen zusätzlich mit einem echten Browser im Handy-Format nachsehen (Playwright, Viewport 390×844) und einen Screenshot machen — Text im Quelltext beweist noch nicht, dass es auch richtig aussieht.

Der Cloudflare-Worker (Altbestand)

Bleibt vorerst bestehen, liefert aber nur noch …workers.dev aus. Ein Deploy dorthin ändert an der echten Seite nichts. Der Versuch, die Domain per wrangler wieder anzubinden, schlägt bewusst fehl:

Hostname 'dogfather-universe.com' already has externally managed DNS records

Das ist kein Fehler, den man beheben sollte — es ist die Schutzwirkung davon, dass die Domain jetzt auf Netcup zeigt. routes mit custom_domain = true steht deshalb nur noch aus historischen Gründen in wrangler.toml.

Der Bewerbungs-Postfach-Worker ist davon unabhängig und läuft weiter auf Cloudflare: https://dogfather-universe-postfach.dogfather1608.workers.dev (API_BASE_URL in assets/js/forms.js, index.html, postfach.html).

Git-Ablage

  • Quelle der Wahrheit: Gitea, git.dogfather-universe.com/DogFatherGit/dogfather-universe, Zweig main (auf dem eigenen Server, kostenlos, in eigener Hand).
  • Lokal heißt das Fernziel gitea, der Zweig master.
  • Auf dem Server heißt das Fernziel origin, der Zweig main.
  • gitea/master ist ein alter, abgehängter Zweig (Stand 44086b0) — nicht benutzen.
  • Sicherungszweig server-stand-vor-abgleich-22-08-2026 auf dem Server: der Stand, bevor die beiden Fassungen am 22.08.2026 zusammengeführt wurden. Kann irgendwann weg, kostet nichts.

Nie direkt auf dem Server Dateien bearbeiten, ohne sie danach in Gitea nachzutragen. Genau dadurch waren am 22.08.2026 zwei Reparaturen (Autofokus in gate.html, abgeschnittene Öffnen-Knöpfe in verwaltung.html) nur auf dem Server vorhanden und wären beim nächsten Deploy vom Rechner aus überschrieben worden.

Offene Punkte

  • Server-Neustart steht aus (seit 28.08.2026). unattended-upgrades hat Kernel (6.12.105) und OpenSSL (3.5.7) eingespielt, aber es läuft noch der alte Kernel (6.12.100) und die Dienste haben die alte libssl im Speicher. /var/run/reboot-required steht. Kein Notfall (die OpenSSL-CVEs betreffen PKCS7/CMS/QUIC-Pfade, die hier kaum aktiv sind — Caddys QUIC läuft über Go), aber der Neustart gehört auf eine ruhige Zeit gelegt, während Filipe erreichbar ist (Netcup-Konsole griffbereit). Danach prüfen: laufen alle sechs Dienste?

  • Server-internal-Deploy ausstehend (28.08.2026): In Gitea liegen fertige, getestete Änderungen an server-internal/, die noch nicht live sind (Anfragebremse für /submit + /testimonials/submit; package-lock.json auf multer 2.2.0 / node-cron 4.6.0). Ausrollen mit dem vollen Deploy-Block oben (der mit npm ci), weil sich die Paketstände geändert haben. Getestet: multer 2.2.0 fängt den DoS-Fall sauber ab, node-cron 4.x akzeptiert den bestehenden Aufruf, alle Selbsttests grün.

  • Hintergrundbilder liegen nur auf dem Server — erledigt, nachgeprüft am 26.08.2026. Alle genannten Dateien (bg-bewerben-modi.jpg, bg-links-seite.jpg, bg-medien-casper.jpg, bg-supporter.jpg, bg-modis.jpg) und der Ordner assets/img/stimmen-avatare/ (8 Dateien) sind inzwischen versioniert. git status --untracked-files=all -- assets/ auf dem Server meldet null unverfolgte Dateien.

    Der Eintrag bleibt hier stehen, statt gelöscht zu werden: Eine Liste offener Punkte, in der Erledigtes ungekennzeichnet steht, wird beim nächsten Mal gar nicht mehr gelesen. Wer prüft, will sehen, dass geprüft wurde.

  • CORS steht auf * — in server-internal/index.js (app.use(cors())) und im Altbestand cloudflare-worker/src/lib/http.js.

    Eingeordnet am 26.08.2026, weniger dringend als der alte Hinweis klang: Die Antwort enthält kein Access-Control-Allow-Credentials, und die Verwaltung weist sich über Authorization: Bearer … aus, nicht über ein Cookie. Ein Browser schickt bei einer fremden Seite deshalb weder Cookies noch das Token mit — eine fremde Seite erreicht damit nur die ohnehin öffentlichen Endpunkte (Team, Events, Stimmen). An Kundendaten kommt sie nicht.

    Sauberer wäre es trotzdem: eine feste Liste erlaubter Herkünfte (dogfather-universe.com, vans-diy-bastelbedarf.com) statt *. Das ist Härtung, keine Reparatur — und ein Eingriff in einen laufenden Dienst, der bei zu enger Einstellung die Seite lahmlegt. Also als eigener Vorgang, mit Live-Test danach.

  • express 5 vorbereitet, nicht umgestellt. pruef-express5.mjs (Bestand durchsuchen) und server/test-express5.mjs (echter Server gegen 5.2.1) sind grün — der Umstieg wäre ohne Codeänderung möglich. Nicht durchgeführt, weil express 4.22.2 gepflegt wird und npm audit null meldet. Ebenfalls offen: dotenv 16→17, better-sqlite3 11→13 (dort muss die ABI zur Node-Fassung passen; ein Fehlgriff legt den internen Dienst still).

  • Datenschutz-Endpunkte warten auf einen Pull — erledigt, live geprüft am 28.08.2026. Alle drei (/webdesign/admin/datenschutz/{auskunft,vorschau, loeschen}) antworten auf der echten Domain mit 401 (vorhanden und geschützt), nicht mehr 404. Steht abgehakt hier, nicht gelöscht — wer prüft, will sehen, dass geprüft wurde.

Alternative Hosting-Optionen (nur als Notfall-Plan)

Die Seite ist reines statisches HTML/CSS/JS und läuft überall — netlify.toml und .nojekyll liegen bereit. Relevant nur, falls der Netcup-Server einmal ausfällt.