Files
nexarch/archive/docs/BAK-08-PRUEFPROTOKOLL.md
T
sysops 67bcdd1833 feat(archive): BAK-08 Checksum-basierte Objekt-Integritaetspruefung
Stichprobenbasierter Scrub-Job: nimmt BAK-05s existing_in_storage,
priorisiert nach eigenem scrub_state.last_scrubbed_at (nicht
file_revisions.created_at, sonst kein echtes Rotationsverhalten),
prueft Inhalt per SHA-256 gegen file_revisions.checksum_sha256.
Meldung ueber echten dauerhaften /metrics-Endpunkt (Pull-Modell,
OPS-03 scrapt, kein Push), Counter monoton steigend. Real registriert
in Core metrics_sources, End-zu-Ende ueber OPS-03-Aggregator bestaetigt,
realer Befund-Durchlauf mit absichtlich falscher Pruefsumme durchgefuehrt.
2026-08-29 23:55:24 +02:00

7.9 KiB
Raw Blame History

BAK-08 Prüfprotokoll: Checksum-basierte Objekt-Integritätsprüfung

Voraussetzungen BAK-05, FDN-04, FDN-09, OPS-05 alle erledigt, siehe eigene Protokolle. Vor Start zwei offene Rückfragen geklärt (siehe unten).

Grundsatzentscheidung: eigener Zustand statt file_revisions.created_at

created_at als Alterskriterium hätte immer dieselben "ältesten" Objekte gescrubbt und den Rest nie erreicht — kein echtes Rotationsverhalten. Stattdessen eigene Archive-Tabelle scrub_state (storage_keylast_scrubbed_at, last_result), Migration migrations/0001_scrub_state.up.sql. internal/scrub.Sample ist eine reine Funktion: nimmt BAK-05s existing_in_storage (deterministisch sortiert) entgegen, filtert Objekte innerhalb der konfigurierbaren Cooldown-Frist heraus, priorisiert danach nach last_scrubbed_at aufsteigend (nie geprüft = ältestmöglicher Wert), begrenzt auf die konfigurierte Stichprobengröße — kein Voll-Sort über den gesamten Bestand bei jedem Lauf (Nutzerhinweis zum Kostenfaktor bei 10⁵+ Objekten: die WHERE-artige Cooldown-Filterung reduziert die Kandidatenmenge VOR der Sortierung, nur die Kandidaten selbst werden sortiert, nicht der komplette Bestand).

Nachtrag: zwei Rückfragen vor Implementierungsbeginn geklärt

  1. OPS-05-Anbindung ist Pull, nicht Push. OPS-05 (internal/alerting, Core) ist real implementiert, aber Core OPS-03 scrapt /metrics-URLs registrierter Module (metrics_sources-Tabelle in der Core-Registry- DB, SourceStore.RegisterSource) — kein Push-API. Für BAK-08 daher ein eigener, DAUERHAFT laufender Endpunkt (cmd/scrub-metrics, getrennt vom Oneshot-scrub-cli, dessen Prozess nach jedem Lauf endet und zum Scrape-Zeitpunkt nicht erreichbar wäre). Metrik als Counter (nexarch_archive_storage_integrity_failures_total), monoton steigend — kein Gauge, kein Rücksetzen bei behobenem Befund. Kein Befund = Wert bleibt unverändert (kein Dauer-Alarm durch andauernden "Fehler"-Zustand). Scope-Trennung gewahrt: scrub-cli/scrub-metrics erzeugen selbst KEIN Alert-Objekt — Schwellwert/Drosselung bleiben OPS-05-eigene Konfiguration (Alert-Regel wird separat über alerting.RuleStore.CreateRule angelegt, nicht Teil dieses Tickets). CFG-04 war eine Verwechslung (das ist die Benachrichtigungs-Einstellungen-Oberfläche, ein anderes Ticket) — die tatsächlich nötige "Config"-Aktion ist ein INSERT in metrics_sources (Core-Registry-DB), kein UI/Ticket-Abhängigkeit. Real ausgeführt (siehe „Echte Verdrahtung" unten).
  2. Sampling-Kriterium. Siehe Grundsatzentscheidung oben — scrub_state.last_scrubbed_at statt file_revisions.created_at, Cooldown-Filterung vor Sortierung, feste Stichprobengröße (Top-N, deterministisch, keine Zufallsstichprobe — Nutzerpräferenz für Reproduzierbarkeit im Protokoll).

Umsetzung

  • migrations/0001_scrub_state.up.sql/.down.sqlscrub_state, scrub_counters (Einzelzeile, monotoner Zähler).
  • internal/scrub.Sample — reine Funktion, Cooldown-Filter + Alt- Priorisierung + Stichprobenbegrenzung.
  • internal/scrub.LoadLastScrubbed/MarkScrubbed/RecordFinding/ FindingsTotal — DB-Zugriff auf scrub_state/scrub_counters, MarkScrubbed idempotent (ON CONFLICT) für unterbrechbare Läufe.
  • internal/scrub.ExpectedChecksums — eigene, minimale Abfrage gegen file_revisions (keine Erweiterung von reconcile.DBEntry — BAK-05 bleibt existenz-only).
  • internal/scrub.ActualChecksum — echtes Lesen der Datei + SHA-256, kein Header-/Größenvergleich.
  • cmd/scrub-cli — Oneshot: BAK-05-Reconcile → Sample → pro Kandidat Checksum-Vergleich → MarkScrubbed + bei Abweichung RecordFinding → JSON-Bericht auf stdout, Exit-Code 1 bei Befunden (gemeldet, nicht automatisch repariert).
  • cmd/scrub-metrics — dauerhafter /metrics-Endpunkt, liest scrub_counters.findings_total.

Prüfungen

# Prüfung Ergebnis
1 Absichtlich veränderter Objektinhalt wird als Abweichung erkannt bestanden — real: Testobjekt mit absichtlich falscher checksum_sha256 in dms_tenant_test angelegt, echte Datei ins Storage-Verzeichnis gelegt, scrub-cli real über systemd ausgelöst: Befund im JSON-Bericht, Exit-Code 1, scrub_counters.findings_total real von 0 auf 1 erhöht (siehe Journal-Auszug unten)
2 Sampling priorisiert alte/nie geprüfte Objekte, nicht neue bestandenTestSample_PrioritizesNeverScrubbedAndOldest: nie geprüftes Objekt kommt vor einem vor 30 Tagen geprüften, dieses vor einem vor 1 Tag geprüften
3 Wiederholter Lauf ohne neue Objekte meldet nichts erneut (kein Spam) / idempotent bei Unterbrechung bestanden — real: zweiter scrub-cli-Lauf direkt nach dem ersten liefert sampled: 0 (Cooldown greift), TestMarkScrubbed_IsIdempotent beweist wiederholtes Markieren ohne Duplikat

Zusätzlich: TestSample_RespectsCooldown, TestSample_LimitsToSampleSize, TestSample_DeterministicForIdenticalInput, TestRecordFinding_IsMonotonicallyIncreasing, TestActualChecksum_MatchesRealFileContent (echter Dateiinhalt, echtes SHA-256), TestExpectedChecksums_ReadsRealFileRevisions (echtes Postgres, kein Mock).

Echte Verdrahtung auf 192.168.1.131

  • scrub-cli, scrub-metrics gebaut nach /opt/nexarch-archive/bin/
  • /etc/nexarch/archive-scrub.env, /etc/nexarch/archive-scrub-metrics.env (0600)
  • Migration real gegen dms_tenant_test angewendet (psql -f migrations/0001_scrub_state.up.sql)
  • nexarch-archive-scrub.timer installiert/aktiviert (täglich 06:00 UTC), nexarch-archive-scrub-metrics.service installiert/aktiviert (dauerhaft, Restart=on-failure) — beide systemctl status: aktiv
  • Reales INSERT in metrics_sources (Core-Registry-DB nexarch_registry): ('archive', 'http://127.0.0.1:8090/metrics') — bestätigt über SELECT * FROM metrics_sources
  • End-to-End über OPS-03 bestätigt: curl http://127.0.0.1:8085/metrics (Core-Aggregator) zeigt nexarch_module_archive_nexarch_archive_storage_integrity_failures_total — reale Umbenennung gemäß OPS-03-Namenskonvention, kein synthetischer Wert
  • Realer Befund-Durchlauf: Testobjekt mit absichtlich falscher Prüfsumme angelegt → scrub-cli real via systemctl start ausgelöst → Befund im Journal, scrub_counters.findings_total real 0→1, sichtbar sowohl auf scrub-metrics als auch über den Core-Aggregator → Testdaten anschließend bereinigt (file_revisions/documents/users-Zeilen gelöscht, scrub_state/scrub_counters zurückgesetzt, Testdatei entfernt)

Build/Test-Ergebnis (192.168.1.131, make check)

go build ./...  -> clean
go vet ./...    -> clean
golangci-lint run ./...  -> 0 issues
go test ./... -p 1 -count=1  -> 4/4 Pakete mit Tests ok (internal/backup, internal/objectbackup, internal/reconcile, internal/scrub), 0 Fehlschläge

internal/scrub-Tests separat mit gesetzter TEST_TENANT_DSN gegen dms_tenant_test verifiziert: 8/8 Tests bestanden.

Bekannte Grenze (aus Ticket übernommen, nicht Teil der Abnahme)

Der Job erkennt Abweichungen nur bei Objekten, die gelesen und erneut geprüft werden können. Ersetzt keine storage-seitige WORM-/ Versionierungsstrategie und keine Zugriffs-/Audit-Logs des Storage-Providers (STORAGE-KONZEPT.md Abschnitt 6.1) — bei extern eingebundenem, nicht-kompatiblem Kunden-Storage (Betriebsmodus 3, ohne Versioning/Object Lock/Audit-Logs) bleibt eine Lücke, die BAK-08 technisch nicht schließen kann.

Gesamtergebnis

Bestanden. Alle sechs Akzeptanzkriterien und alle drei Pflicht- prüfungen real erfüllt — inklusive echtem Ende-zu-Ende-Nachweis über Core OPS-03/OPS-05 (kein Stub, reale /metrics-Registrierung und -Aggregation). Beide vor Implementierungsbeginn gestellten Rückfragen (OPS-05-Anbindungsmechanismus, Sampling-Kriterium) im Protokoll dokumentiert und in der Umsetzung berücksichtigt.