Files
nexarch/mail/docs/ARC-09-PRUEFPROTOKOLL.md
T
sysops 1825387603 feat(mail): ARC-09 Postfach-Quota (unabhängig von Core LIC-05)
mailboxconfig (IMP-07) bekommt eine quota_bytes-Spalte statt einer
eigenen Tabelle — ein Postfach ist bereits eindeutig über
(tenant_slug, name) identifiziert. SetQuotaBytes/LimitBytes, 0 =
unbegrenzt (Standardwert, keine Migration bestehender Postfächer
nötig). LimitBytes erfüllt strukturell quota.LimitProvider.

storage.ArchiveMailboxPrefix (ARC-04-Ergänzung, Präfix ALLER Jahre
eines Postfachs) und storage.UsageCounter: realer Speicherverbrauch
durch echtes S3-Listing im physisch getrennten Mandanten-Bucket
(ARC-06) — kein separat gepflegter Zählerstand.

Neues Paket mail/internal/quota: Checker verbindet LimitProvider und
UsageProvider. Kein konfiguriertes Limit = immer erlaubt (Core-LIC-05-
Quota läuft unabhängig weiter — beide Ebenen bewusst unabhängig
durchgesetzt, bekannter Fehler vermieden).

smtp.QuotaChecker (schmale Schnittstelle, keine Paketkopplung an
quota) wird in handleRcptTo geprüft, VOR der Datenübertragung: 552
(RFC 5321 "exceeded storage allocation") bei Überschreitung, Session
bleibt nutzbar. nil-Checker erhält bisheriges Verhalten unverändert.

Alle drei Pflichtprüfungen mit echten Nachweisen: Quota-Überschreitung
liefert 552, Session bleibt funktionsfähig; ein anderes Postfach
desselben Tenants läuft währenddessen vollständig normal durch;
vollständiger Ende-zu-Ende-Integrationstest gegen reale Postgres- und
MinIO-Instanzen — 5000 echte Bytes abgelegt, real gemessen, Limit
knapp darunter/darüber gesetzt, SMTP reagiert jeweils korrekt auf den
tatsächlichen gemessenen Wert. Dabei einen echten Cleanup-Fehler
gefunden und behoben (defer schloss den Pool vor dem zugehörigen
t.Cleanup, verwaiste Testdaten blieben zurück).

go build/go vet/golangci-lint clean, gesamtes Mail-Modul
regressionsfrei getestet.
2026-09-02 23:47:32 +02:00

5.4 KiB

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 (NOOP250); 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).