Mail-Graveyard/review-snapshot-2026-07-16.md
DonVoo c6de214ac8 Review: Snapshot gruen, normalizeMessageID-Aenderung ist Rollout-Blocker
Snapshot (14-snapshot.go) korrekt: VACUUM INTO vor der Wasserlinie, N aus dem
Schnappschuss, MAX(file_offset+frame_len), kein indexed_bytes, stat nur als
Notbremse. Datensatz-Kreuzpruefung (Zeile 145) war nicht verlangt und ist gut.
Reindex-Erhaltung sauber: Position ist Schluessel, Body-Hash ist Bedingung.
Real geprueft: 228 Dateien, mbox_index 121.861 = Produktion, integrity ok.

BEFUND 1 (Blocker): normalizeMessageID aendert die Identitaet fuer Werte mit
verirrtem '>'. Gemessen: 3 Zeilen copied, 2 mbox_index. Die Reindex-Erhaltung
schuetzt den Index, nicht den Live-Pfad - LegacyMessageID ist ebenfalls
neu-normalisiert. Erster Nachtlauf nach Rollout erzeugt 3 lautlose Dubletten.

BEFUND 2: Snapshot-DB ist 0644 und enthaelt 46 Klartext-Passwoerter; die
mbox-Dateien daneben sind 0600.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:38:33 +02:00

5.7 KiB

Review — Snapshot (14-snapshot.go) + Streaming-Reindex

Geprueft von Claude am 2026-07-16 gegen den nicht-committeten Arbeitsstand.

Urteil

Der Snapshot ist richtig gebaut und darf so committet werden. Die Aenderung an normalizeMessageID darf NICHT ohne Fix ausgerollt werden.

Was gepasst hat

Die zwei Stellen, an denen ich Fehler erwartet habe, sind beide korrekt:

104:  DB.Exec(`VACUUM INTO ?`, databasePath)               // erst der Schnappschuss
120:  collectSnapshotWatermarks(snapshotDB, Cfg.MboxRoot)  // dann N DARAUS
      MAX(i.file_offset+i.frame_len)                       // richtige Wasserlinie
327:  os.Stat(...) -> info.Size() < watermark.Bytes        // stat nur als Notbremse
  • indexed_bytes kommt nirgends vor (die Falle aus backup-brief.md).
  • Reihenfolge stimmt; stat leitet N nicht ab, sondern verweigert nur.
  • Zeile 145 bricht ab, wenn indizierte und abgebildete Datensaetze auseinander- laufen. Das stand nicht im Brief und ist besser als verlangt.
  • Pfad-Ausbruchspruefung (relative == "..") vorhanden.
  • snapshotPathKey senkt nur unter Windows auf Kleinschreibung — richtig herum: auf Linux sind Drafts und drafts zwei echte Dateien und duerfen nicht kollidieren, unter Windows wuerden sie es.
  • Streaming-Reindex (06-mbox.go:293) liest nachrichtenweise ueber bufio statt 9,4 GB in den RAM.

Die Message-ID-Erhaltung im Reindex ist entgegen meinem ersten Verdacht sauber:

if storedIdentity.bodySHA256 != "" && storedIdentity.bodySHA256 == entry.BodySHA256 {
    entry.MessageID = storedIdentity.messageID
}

Die Position ist nur der Nachschlage-Schluessel, der Body-Hash ist die Bedingung. Eine falsche Zuordnung kann keine falsche Uebernahme ausloesen.

Nachgeprueft am realen Snapshot archive-20260716-2250: 228 Archivdateien, mbox_index 121.861 im Snapshot und in Produktion, 121.861 praezise copied-Identitaeten, integrity_check=ok. go vet sauber, go test -count=1 ./backend/ gruen (0,366s).


BEFUND 1 — Identitaetsaenderung, Dublettenrisiko (Blocker fuer Rollout)

 func normalizeMessageID(id string) string {
-	return strings.Trim(strings.TrimSpace(id), "<>")
+	if start := strings.IndexByte(id, '<'); start >= 0 { ... }

normalizeMessageID speist message_id = die halbe Identitaet. Alt und neu gegen die echten gespeicherten Werte verglichen (lauffaehig nachgestellt):

<476BDFDB019289BE@mail21.bluewin.ch> (added by postmaster@bluewin.ch)
  alt: [476BDFDB019289BE@mail21.bluewin.ch> (added by postmaster@bluewin.ch)]
  neu: [476BDFDB019289BE@mail21.bluewin.ch]                    <<< WEICHT AB

<22578196653698419217.BB8A0E8F48F3F290@dr-gold.de>+D271B1409C3C6028
  alt: [22578196653698419217.BB8A0E8F48F3F290@dr-gold.de>+D271B1409C3C6028]
  neu: [22578196653698419217.BB8A0E8F48F3F290@dr-gold.de]      <<< WEICHT AB

<1280874184.3726.1662111901550.JavaMail."LOCAL SERVICE"@DESV226350>   gleich
<476FE4C5.2060001@yahoo.com;Evy O'Brien>                             gleich
<13140-22021132765855760@strepack.de>                                gleich
13140-22021132765855760@strepack.de                                  gleich

Nur Werte mit einem verirrten > im Inneren aendern sich. Umfang gemessen: 3 Zeilen in copied, 2 in mbox_index von 121.861.

Warum die Reindex-Erhaltung nicht reicht: Sie schuetzt den Index, nicht den Live-Pfad. Dort gilt:

  1. Praezise Abfrage message_id=NEU AND body_sha256=X → gespeichert ist ALT → kein Treffer.
  2. Die Alias-Schleife hilft nicht: LegacyMessageID wird in identityForRawMessage ebenfalls mit der neuen Funktion gebildet (id := normalizeMessageID(...); legacyID := id) — sie traegt die alte Normalisierung nicht.
  3. Ergebnis: CopyState{} → nicht kopiert → erneutes mbox.Append UND erneutes dst.Append.

Beim ersten Nachtlauf nach dem Rollout entstehen so 3 Dubletten in beiden Haelften — lautlos. Klein, aber es ist dieselbe Klasse wie die 4.024 Waisen, und Dubletten sind unsere Stopp-Bedingung.

Zwei Wege, Empfehlung (a)

(a) Alte Normalisierung als zusaetzlichen Alias fuehren. identityForRawMessage bekommt zusaetzlich den alt-normalisierten Wert; die Alias-Schleife in GetCopyIdentityStateWithFolderAlias muss ihn dann auch gegen praezise Zeilen pruefen, nicht nur gegen body_sha256=''. Additiv, mutiert keine Geschichte, nutzt vorhandene Maschinerie. Passt zur Linie „Aliase sind Rueckrollschutz".

(b) Einmal-Migration der 5 Zeilen auf den neuen Wert. Simpler, aber schreibt Geschichte um — und die Regel lautet: Aliase nicht ohne gesonderte, geprueefte Bereinigung anfassen.

Abnahme fuer den Fix

  1. Lauf gegen ein Konto mit einer der beiden IDs: archived=0. Ein zweiter Lauf danach ebenfalls archived=0.
  2. SELECT COUNT(*) FROM mbox_index WHERE message_id LIKE '%>%' bleibt bei 2 — nicht 4.
  3. Test mit genau diesen zwei Roh-Headern als Fixture.

BEFUND 2 — Snapshot-DB world-readable (klein, aber schnell zu beheben)

600 root:root  .../backup/colak@dr-gold.de/INBOX.mbox   <- richtig
644 root:root  .../data/mail-graveyard.db               <- zu offen

Die Snapshot-DB enthaelt 23 Quell- und 23 Ziel-Passwoerter im Klartext. Heute schuetzt nur die 0700-Wurzel darueber — ein chmod von der Preisgabe entfernt, und inkonsistent zu den mbox-Dateien daneben. Bitte 0600.


Nicht deins

gofmt -l meldet backend/11-folders.go. Die Datei ist im Diff unveraendert (Altbestand), reine Ausrichtung einer Map. Kein Rollout-Hindernis.

Weiterhin offen (unveraendert aus backup-brief.md)

  • Der Snapshot liegt auf derselben Platte — er ist die Vorstufe, nicht das Backup. Raeumlich getrennte, verschluesselte Kopie fehlt.
  • Restore-Test gehoert wiederholt gefahren, nicht einmal.