Der Absturz auf dem Server bestand nach dem ersten Fix fort:
node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr
Statement::~Statement() … better_sqlite3.node
Abgebrochen
DER ERSTE VERSUCH GING AN DER URSACHE VORBEI
Ich hatte db.close() entfernt -- naheliegend, weil der Aufrufverlauf
auf einen Statement-Destruktor zeigte. Es half nicht. Die Ursache liegt
eine Ebene tiefer: process.exit() beendet Node SOFORT, waehrend
better-sqlite3 noch offene Statements haelt. Deren Aufraeumhaken laeuft
dann ins Leere.
process.exitCode setzt nur den Rueckgabewert; Node beendet sich danach
von selbst, sobald nichts mehr aussteht -- und raeumt dabei in der
richtigen Reihenfolge auf.
WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER WAR
Der Absturz kam NACH allen Pruefungen und VOR der Zusammenfassung. Der
Test meldete einen Fehler, obwohl inhaltlich alles bestanden war. In
einer mit && verketteten Befehlsfolge blieb deshalb der anschliessende
Dienst-Neustart aus, und die neuen Endpunkte antworteten weiter mit
404. Gesucht habe ich bei den Endpunkten, beim Deploy, an der
Zugangswand -- die Ursache lag beim Beenden eines Testprozesses.
BEMERKENSWERT
Fuenf Tests im Projekt benutzten process.exitCode bereits. Das Muster
war also etabliert; meine neuen Dateien wichen davon ab, ohne dass es
jemandem auffiel. Sechs Tests sind jetzt angeglichen, alle geprueft:
Rueckgabewert 0, Zusammenfassung vollstaendig.
test-push-kette 30, test-altabbruch 16, test-webdesign-anfragen 37,
test-webdesign-portal 49, test-webdesign-paypal 28,
test-personendaten 15
Co-Authored-By: Claude Opus 5 <[email protected]>
Auf dem Server brach test-personendaten.mjs ab:
node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr
Statement::~Statement() … better_sqlite3.node
Abgebrochen
better-sqlite3 raeumt seine Statements ueber einen Aufraeumhaken ab.
Wird die Verbindung unmittelbar vor dem Prozessende geschlossen, laeuft
dieser Haken ins Leere.
DIE FOLGE WAR SCHLIMMER ALS DER ABSTURZ
Der Absturz kam NACH allen Pruefungen, aber VOR der Zusammenfassung.
Der Test lieferte also einen Fehlercode, obwohl inhaltlich alles
bestanden war. In der mit && verketteten Befehlsfolge blieb deshalb der
anschliessende Dienst-Neustart aus -- und die neuen Endpunkte
antworteten weiter mit 404.
Man sucht dann den Fehler bei den Endpunkten, beim Deploy, an der
Zugangswand. Die Ursache lag beim Aufraeumen einer Testdatenbank.
Node schliesst die Verbindung beim Beenden ohnehin. Zusaetzlich faengt
das Loeschen der Testdateien jetzt Fehler ab: Ohne db.close() haelt der
Prozess sie noch offen, unter Windows scheitert das Loeschen dann mit
EBUSY -- und "force" hilft dagegen nicht, es unterdrueckt nur "Datei
nicht gefunden". Ein Test, der an seinem eigenen Aufraeumen scheitert,
meldet einen Fehler, den es fachlich nicht gibt.
Beide betroffenen Tests geprueft: Rueckgabewert 0, Zusammenfassung
vollstaendig.
Co-Authored-By: Claude Opus 5 <[email protected]>
BEFUND ZUERST, DENN ER WAR ANDERS ALS ERWARTET
Es gab sehr wohl eine Benachrichtigung -- ueber einen Discord-Webhook,
sauber gebaut, bewusst ohne Kundendaten im Text. Sie begann aber mit:
const url = process.env.DISCORD_WEBHOOK_WEBDESIGN;
if (!url) return;
Und diese Variable ist in .env.example nirgends aufgefuehrt. Eine
Variable, die niemand kennt, wird nicht gesetzt; dann kehrte die
Funktion wortlos zurueck. Kein Fehler, keine Protokollzeile. Es gab
eine Benachrichtigung, die nur im Quelltext existierte -- und niemand
konnte das bemerken, weil "funktioniert" und "kaputt" identisch
aussehen, solange nichts passiert.
WAS JETZT DA IST
Push aufs Handy, ohne Fremdpaket. Der interne Dienst liegt unter
/home/dogiintern mit Rechten 700 -- dort ist kein npm erreichbar, jede
Abhaengigkeit haette dauerhafte Handarbeit bedeutet. Node bringt P-256,
HKDF und AES-128-GCM selbst mit.
Die Schluessel erzeugt der Server beim ersten Start selbst und legt den
privaten Teil verschluesselt in der Datenbank ab (derselbe Weg wie die
PayPal-Zugangsdaten). Damit gibt es keinen Einrichtungsschritt, der
vergessen werden kann.
GEPRUEFT
Gegen RFC 8291 statt gegen ein Bauchgefuehl: alle fuenf Zwischenwerte
aus Anhang A stimmen (ECDH-Geheimnis, PRK_key, IKM, CEK, NONCE), und
die fertige Nachricht ist Byte fuer Byte die aus Abschnitt 5 der Norm.
Bei Kryptographie erzeugt ein Ableitungsfehler keinen Absturz, sondern
Bytes, die genauso zufaellig aussehen wie richtige.
Dazu ein Kettentest mit einem echten Empfaenger, der entschluesselt:
Kopfzeilen, Inhalt, keine Kundendaten in der Meldung, 410 loescht das
Geraet, 500 loescht es NICHT (sonst kostet eine einzelne Stoerung die
Anmeldung). 50 Pruefungen, alle gruen.
DREI ENTSCHEIDUNGEN
1. In der Meldung stehen nur Nummer und Paket. Sie erscheint auf einem
Sperrbildschirm, den auch jemand sieht, der zufaellig danebensteht.
2. Ist der Schluessel unlesbar, wird KEIN neuer erzeugt. Das waere der
bequeme Weg und der schlimmste: Ein neuer oeffentlicher Schluessel
macht schlagartig jede Anmeldung wertlos, ohne dass jemand erfaehrt,
warum nichts mehr ankommt.
3. Die Verwaltung zeigt den Zustand an und hat einen Testknopf. Genau
das fehlte dem Discord-Weg. Bei verweigerter Erlaubnis erscheint
kein Knopf, der nichts bewirkt, sondern der Weg ueber die
Browsereinstellungen.
Das stille "if (!url) return;" ist ersetzt: Jeder Weg wird einzeln
protokolliert -- auch und gerade, wenn er uebersprungen wird.
Co-Authored-By: Claude Opus 5 <[email protected]>