# ARC-09 — Postfach-Quota: Prüfprotokoll Datum: 2026-09-02 Host: 192.168.1.131 (Build/Test/Lint), rsync + ssh Pakete: `mail/internal/quota` (neu), `mail/internal/storage` (`usagecounter.go`, `archivekey.go` erweitert), `mail/internal/mailboxconfig` (erweitert), `mail/internal/smtp` (erweitert) ## Umsetzung **Konfiguriertes Limit** (Akzeptanzkriterium 1): `mailboxconfig` (IMP-07) bekommt eine neue Spalte `quota_bytes` (Migration `0002_mail_mailboxes_quota.sql`, idempotent nachgezogen) statt einer eigenen Tabelle — ein Postfach ist bereits eindeutig über `(tenant_slug, name)` identifiziert. `Store.SetQuotaBytes`/`LimitBytes` (`0` = unbegrenzt, Standardwert, keine Migration bestehender Postfächer nötig). `LimitBytes` erfüllt strukturell `quota. LimitProvider` — eigenständig von der tenant-weiten Core-LIC-05-Quota. **Realer Verbrauch** (Pflichtprüfung 3): `storage.UsageCounter` summiert die TATSÄCHLICHE Objektgröße aller Objekte unter `storage.ArchiveMailboxPrefix(mailbox)` (neu, ARC-04-Ergänzung — Präfix ALLER Jahre eines Postfachs) im physisch getrennten Mandanten-Bucket (ARC-06) — kein separat gepflegter Zählerstand, der von der tatsächlichen Ablage abweichen könnte. Erfüllt strukturell `quota. UsageProvider`. **Verknüpfung**: `quota.Checker` (neues Paket) verbindet `LimitProvider` und `UsageProvider`: kein konfiguriertes Limit = immer erlaubt (Core-LIC-05-Quota läuft unabhängig weiter, bekannter Fehler bewusst vermieden — beide Ebenen unabhängig durchgesetzt). **SMTP-Durchsetzung** (Akzeptanzkriterium 2): `smtp.QuotaChecker` (schmale Schnittstelle, keine Paketkopplung an `quota`) wird in `handleRcptTo` geprüft — VOR der Datenübertragung, nicht erst nach vollständigem DATA-Empfang. Bei Überschreitung: `552` (RFC 5321 "exceeded storage allocation"), Session bleibt nutzbar. Der Empfänger (RCPT-TO-Adresse) ist der Postfachbezug — dasselbe `mailbox`-Feld wie `storage.ArchiveKey`/`mailboxconfig`. `quotaChecker == nil` erhält das bisherige Verhalten unverändert (Rückwärtskompatibilität zu ING-01..QA-04). ## Pflichtprüfung 1: Postfach-Quota erreicht, neue eingehende Mail wird mit korrekter SMTP-Fehlermeldung abgelehnt `TestRcptTo_QuotaExceededRejectedWithCorrectSMTPError`: RCPT TO an ein als "am Limit" markiertes Postfach liefert `552` mit erkennbarer Quota-Fehlermeldung; Session bleibt danach funktionsfähig (`NOOP` → `250`); der Sink bekommt keine Nachricht. Ergebnis: **BESTANDEN**. ## Pflichtprüfung 2: anderes Postfach desselben Tenants empfängt weiterhin normal, während eines am Limit ist `TestRcptTo_OtherMailboxUnaffectedWhenOneAtLimit`: zwei unabhängige SMTP-Transaktionen desselben Tenants — die erste (Postfach am Limit) wird mit `552` abgelehnt, die zweite (anderes Postfach, kein Limit) läuft vollständig durch (`250`/`354`/`250`), die Nachricht kommt real beim Sink an. Ergebnis: **BESTANDEN**. ## Pflichtprüfung 3: Verbrauchsanzeige je Postfach im Test korrekt gegen tatsächliche Größe geprüft `TestIntegration_UsageDisplayMatchesRealSizeAndEnforcesQuota` (vollständiger Ende-zu-Ende-Integrationstest, echte Postgres- und MinIO-Instanz): 5000 Bytes real in den ARC-06-Bucket eines real provisionierten Mandanten geschrieben, `storage.UsageCounter. UsageBytes` gemessen — der gemessene Wert liegt bei/über der tatsächlich geschriebenen Größe (das Prüfsummen-Sidecar-Objekt aus ARC-01 zählt strukturell mit, daher `>=` statt `==` geprüft). Limit knapp UNTER dem real gemessenen Verbrauch gesetzt → `RCPT TO` liefert real `552`; Limit anschließend großzügig ÜBER den Verbrauch erhöht → dieselbe Adresse liefert danach real `250` — die Quota-Durchsetzung reagiert korrekt auf den ECHTEN, gemessenen Wert, nicht auf einen angenommenen. Ergebnis: **BESTANDEN** (inklusive eines während der Testentwicklung gefundenen und behobenen Cleanup-Fehlers: `defer pool.Close()` schloss die Postgres-Verbindung VOR den zugehörigen `t.Cleanup`-Löschungen, wodurch verwaiste Registry-/Postfach-Zeilen zurückblieben — behoben durch `t.Cleanup(pool.Close)` statt `defer`, LIFO-Reihenfolge stellt sicher, dass Löschungen vor dem Verbindungsschluss laufen; durch zwei aufeinanderfolgende reale Testläufe bestätigt). ## Akzeptanzkriterien 1. **Speicherlimit ist je Postfach konfigurierbar, unabhängig von der Tenant-weiten Quota aus Core LIC-05**: `mailboxconfig. SetQuotaBytes`/`LimitBytes`, durch Pflichtprüfung 3 belegt. 2. **Postfach am Limit lehnt neue eingehende Mail mit klarer, protokollgerechter SMTP-Fehlermeldung ab**: `552` bei `RCPT TO`, durch Pflichtprüfung 1 belegt. 3. **Ein Postfach am Limit beeinträchtigt keine anderen Postfächer desselben Tenants**: durch Pflichtprüfung 2 belegt. ## Build/Vet/Lint/Test — Gesamtmodul ``` go build ./... → OK go vet ./... → OK golangci-lint run ./... → 0 issues go test ./... -p 1 (TEST_TENANT_DSN, TEST_MANTICORE_URL, TEST_S3_ENDPOINT/TEST_S3_ACCESS_KEY/TEST_S3_SECRET_KEY gesetzt) → alle Pakete ok, inkl. neuem internal/quota ``` Keine Regression — insbesondere bestehende `mailboxconfig`-Tests (IMP-07) bleiben nach der neuen `quota_bytes`-Spalte unverändert grün. ## Ergebnis ARC-09 erfüllt alle Akzeptanzkriterien mit echten, ausgeführten Nachweisen — inklusive eines vollständigen Ende-zu-Ende-Integrations- tests gegen reale Postgres- und MinIO-Instanzen. Freigeschaltet: QA-05 (zusammen mit ARC-07/10/INT-08, ARC-05 weiterhin extern blockiert durch RET-03).