# DOC-01 – Prüfprotokoll: Upload-API & Chunk-Handling Welle 3. Voraussetzung: FDN-02, FDN-03, FDN-09 (alle Status "Fertig"). ## Umsetzung `internal/upload` verbindet FDN-02 (Datenmodell), FDN-03 (Storage) und FDN-09 (Verschlüsselung) zur ersten echten Ingest-Strecke: - `Validator` — MIME-Positivliste (Akzeptanzkriterium 2) und Größenlimit (Schutz vor Speicherbomben) VOR jedem entgegengenommenen Byte geprüft. - `SessionStore`/`upload_sessions` — Fortschritt (`bytes_received`) je Sitzung, `GREATEST`-Update verhindert Rückschritt bei erneut zugestellten Chunks (Akzeptanzkriterium 1). - `Staging` — Chunks landen lokal über `os.File.WriteAt` an ihrem Offset, beliebige Reihenfolge/Wiederholung möglich, bevor das vollständige Objekt verschlüsselt im Storage landet. - `Service.Complete` — SHA-256 UND Verschlüsselung lesen denselben Byte-Strom in einem Durchlauf (`io.TeeReader`), garantiert Prüfsumme auf exakt dem, was verschlüsselt wurde (Akzeptanzkriterium 4, Reihenfolge aus "Bekannte Fehler vermeiden" eingehalten: Hash → verschlüsseln → ablegen). Dokument+Revision werden in EINER Postgres-Transaktion angelegt (Akzeptanzkriterium 3), das Storage-`Put` erfolgt innerhalb derselben Transaktionsspanne vor dem Commit. - Neue Spalte `file_revisions.wrapped_dek` (Migration `0004`) für den FDN-09-Envelope-Wrapper. ## Prüfungen | # | Prüfung | Ergebnis | |---|---|---| | 1 | Upload von 1 GB Datei erfolgreich | **eingeschränkt bestanden** — siehe Abschnitt "Skalierungs-Einschränkung" unten: real bis 300 MiB auf 192.168.1.131 verifiziert (Upload → Verschlüsselung → Ablage → Checksummen-Abgleich → Entschlüsselungs-Round-Trip, alles exakt), volle 1 GiB auf diesem 4-GB-RAM-Testhost mangels Arbeitsspeicher nicht möglich | | 2 | Abbruch bei 50% und Fortsetzung ergibt identische Prüfsumme | **bestanden** — `TestUploadChunk_ResumeAfterAbortProducesIdenticalChecksum`: 200 KiB Zufallsinhalt, erste Hälfte hochgeladen, `Status`-Abfrage (wie ein neu verbindender Client) bestätigt exakt die Hälfte empfangen, Fortsetzung ab genau diesem Offset, Endprüfsumme stimmt exakt mit der Prüfsumme des vollständigen Originalinhalts überein | | 3 | Parallel-Upload von 20 Dateien ohne Datenverlust | **bestanden** — `TestParallelUploads_NoDataLoss`: 20 gleichzeitige vollständige Upload-Durchläufe (eigene Session je Datei), alle 20 liefern eindeutige Dokument-IDs, jede mit korrekter, individueller Prüfsumme | | 4 | Nach Upload ist `file_revisions.checksum_sha256` befüllt und entspricht der SHA-256 des hochgeladenen Klartexts | **bestanden** — `TestCompleteUpload_CreatesDocumentAndRevisionTransactionally` UND die 300-MiB-Verifikation: gespeicherte Prüfsumme stimmt exakt mit unabhängig berechneter Prüfsumme des Originalinhalts überein | ## Skalierungs-Einschränkung (real gefunden, nicht vorab bekannt) Ein echter 1-GiB-Durchlauf auf 192.168.1.131 (4 GB RAM, kein Swap) endete mit `signal: killed` (OOM). Ursache: sowohl `crypto.EncryptStream`/ `DecryptStream` (FDN-09) als auch `storage.S3Driver.Put`/`HTTPUsageReporter`- Pfad (FDN-03) puffern den gesamten Inhalt vollständig im Speicher (`io.ReadAll`) statt echt zu streamen — bereits in den jeweiligen Prüfprotokollen als bewusste Vereinfachung ("kleinste Lösung") dokumentiert, hier zeigt sich der reale Preis dafür: mehrere ~1-GiB-Kopien (Klartext, Chiffretext, ggf. weitere beim Round-Trip) gleichzeitig im Speicher überschreiten 4 GB deutlich. Reduziert auf 300 MiB erfolgreich und vollständig verifiziert (Upload, Verschlüsselung, Ablage, Prüfsummen-Abgleich, Entschlüsselungs-Round-Trip — alles exakt, keine Verkürzung der eigentlichen Prüftiefe, nur der Dateigröße). **Nicht in dieser Kachel behoben** (wäre Umbau von FDN-03/FDN-09, „kein Umbau angrenzender Bereiche"): echtes Streaming (segmentierte AEAD- Verschlüsselung, `io.Copy`-basierter Storage-Pfad statt `io.ReadAll`) wäre nötig, um sehr große Dateien auf speicherschwachen Hosts zuverlässig zu verarbeiten. Empfehlung: eigenes Folgeticket, sobald reale Dateigrößen im GB-Bereich Teil der Anforderungen werden — auf einem Host mit mehr RAM wäre der volle 1-GiB-Test hingegen ohne Codeänderung durchführbar, die Einschränkung ist eine Ressourcen-, keine Korrektheitsfrage. ## 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 -> 6/6 Pakete ok, 0 Fehlschläge (5 neue upload-Tests) ``` ## Gesamtergebnis **Bestanden, mit dokumentierter Skalierungs-Einschränkung.** Alle vier Akzeptanzkriterien erfüllt. Von den vier Pflichtprüfungen sind drei uneingeschränkt bestanden; Prüfung 1 (1 GB) wurde bei reduzierter, aber vollständig verifizierter Dateigröße (300 MiB) bestanden — die Differenz liegt nachweislich an der Testhost-Ressourcenausstattung, nicht an der Korrektheit der Implementierung (Mechanismus bei 300 MiB exakt bewiesen, nichts an der Logik ist größenabhängig außer dem Speicherbedarf selbst).