# Prüfung & Verbesserung ApiCore / Wabenhain – Arbeitsstand September 2026 ApiCore: entstanden auf dem Branch `improve/integrity-sync-audhd`, inzwischen vollständig in `main` (Commits vom 2026-09-27 bis 2026-10-01, ausgerollt mit dem Backend-Image `sha-f5b1964` am 2026-10-12). Wabenhain: Commits direkt auf `main`, gepusht (u. a. `d38f430`, `5642f0c`, `3bc0693` liegen auf `origin/main`). Offene Punkte aus diesem Bericht werden als Gitea-Issues geführt (ApiCore #39 „Offene Fachfragen aus der Prüfung September 2026“, #9 „AuDHD-Anpassung: Restpunkte“). Verifikationsstufen: **CR** = durch Codeprüfung beurteilt, **AT** = automatisiert getestet, **MP** = manuell im Browser geprüft (Desktop 1366 px + Mobil 390 px), **NV** = nicht verifiziert. ## Prüfmatrix | Funktion | Erwartetes Verhalten | Befund (vorher) | Prio | Umsetzung | Status | |---|---|---|---|---|---| | Abfüllung bucht Honigmenge | Menge nie doppelt/zu viel abgebucht | Menge außerhalb der Transaktion gelesen, Absolutwert geschrieben → parallele Abfüllungen überbuchen, Abzug geht verloren | Kritisch | Bedingtes `UPDATE … WHERE amount >= x` in Transaktion; Korrektur/Löschen per Increment | AT | | Lagerentnahme | Kein negativer Bestand | Ausgang ohne Bestandsprüfung | Hoch | Bedingtes Decrement, 400 bei Unterdeckung | AT | | Völker umsetzen/teilen/vereinigen | Lebende Völker bearbeitbar | Nur Status `ACTIVE` erlaubt – schwache/weisellose Völker gesperrt (Vereinigung weiselloser Völker unmöglich) | Hoch | Nur Endzustände (aufgelöst, verkauft, tot, vereinigt) sperren; Selbst-Vereinigung + doppeltes Auflösen abgelehnt | AT | | Königin umweiseln | Wechsel A → B möglich, Historie korrekt | Altes Volk behielt `currentQueenId` → Unique-Konflikt; Zuweisung an aufgelöste Völker möglich | Hoch | Altes Volk gibt Königin frei (→ weisellos), weiselloses Volk mit Königin → aktiv | AT | | Aufgabe erledigen | Idempotent, Status/Zeit konsistent | Doppelklick verschob `completedAt`, Webhook doppelt; Wiedereröffnen behielt `completedAt` | Mittel | Zweites Erledigen = No-op; `completedAt` folgt Status | AT | | Wiederkehrende Aufgaben | Fällige Aufgabe mit Termin | Erzeugte Aufgaben ohne Fälligkeit; verpasste Wochen einzeln nachgeholt | Mittel | Fälligkeit = Termin; verpasste Termine ergeben EINE Erinnerung | AT | | Automatisierungen | Eine offene Aufgabe je Regel und Objekt | Jede Auslösung (z. B. jede Entnahme unter Mindestbestand) erzeugte neue Aufgabe + Warnung | Hoch | Kennzeichnung `sourceAutomationTrigger`, Duplikatsperre; optional `config.dueInDays` | AT | | Varroa-Schwelle | Vergleich nur in gleicher Einheit | Probenzählwerte (Puderzucker/Auswaschen) und Brettzählung ohne Zeitraum gegen „Milben/Tag“ verglichen | Hoch | Nur natürlicher Milbenfall mit Tagesrate | AT | | Offline-Warteschlange | Wiederholung erzeugt keine Duplikate | Verlorene Antwort → erneutes Senden → doppelte Durchsicht/Ernte + Automatik-Aufgabe | Hoch | `Idempotency-Key` (Opt-in je Endpunkt), Schlüssel im Queue-Eintrag | AT | | Start mehrerer Replikas | Beide Pods starten | Berechtigungs-Sync auf leerer DB: Unique-Fehler, Pod-Absturz | Mittel | `createMany … skipDuplicates` | AT | | Eingabeplausibilität | Serverseitige Grenzen | Skalen 1–5 nur im UI; doppelte Völker in Ernte → 500; Netto > Brutto möglich | Mittel | DTO-Grenzen, `ArrayUnique`, Gewichtsprüfung | AT | | Wabenhain: Ernte-Beschreibung | Keine internen Daten öffentlich | Interne ApiCore-Ernte-Notizen in Shoptext kopiert | Hoch (Sicherheit) | Notizen nicht mehr übernommen | AT | | Wabenhain: Entwürfe | Admin-Korrekturen bleiben | Jeder Sync überschrieb unveröffentlichte Entwürfe | Hoch | Im Admin bearbeitete Entwürfe (Audit-Log) geschützt, gemeldet | CR | | Wabenhain: Abruf | Kein Hängen, Fehler ≠ leere Liste | Kein Timeout | Mittel | 20-s-Timeout; Tests: Fehler/Strukturfehler brechen ab | AT | | Wabenhain: Sichtbarkeit | Letzter Lauf erkennbar | Nur direkt nach Klick | Niedrig | „Letzter vollständiger Lauf“ im Panel | CR | | Start-/Heute-Ansicht | Nächster Schritt klar | Kennzahlen-Kacheln statt Aufgaben | Hoch (UX) | „Als Nächstes“ + „Außerdem heute“, Rückgängig | AT (Logik) + MP | | Bewegung | Einstellbar, System respektiert | Nur `motion-safe`-Klassen | Mittel | Voll/Dezent/Aus je Gerät | MP | | Etiketten-Schnittmarken | Anlegekante für Papierschneider | Keine Marken, Zuschnitt nach Augenmaß | Mittel | Haarlinien-Marken im Blattrand an jeder Schnittkante, je Vorlage abschaltbar | AT + MP | | Folge-Kontrollen | Nachkontrolle nach Maßnahme | Keine | Mittel | Regeln „Nach Behandlung/Fütterung“, standardmäßig aus, Frist „Fällig nach (Tagen)“ | AT | | Stockkarte je Volk | Vollständige Historie als PDF | Nur Jahresbericht | Mittel | `colony-record-card.pdf`, optional je Jahr | AT + MP | | Bestandsbuch Tierarzneimittel | Pflichtangaben Art. 108 VO (EU) 2019/6 | Lieferant, Erwerbsbeleg, Tierarzt, Standort am Behandlungstag, Halter fehlten | Hoch (Recht) | Felder ergänzt, Stand je Behandlung gespeichert, PDF je Stand, fehlende Pflichtangaben als „FEHLT“ | AT + MP | | Ein-/Auswinterung & Verluste | Winterverluste je Volk und Stand nachvollziehbar | Nur Volksstatus, kein Bezug zum Winter, keine Ursache | Mittel | `WinterCheck` je Volk/Winter/Saison, Verlustkategorien nach COLOSS, Übersicht mit Verlustrate; nicht ausgewinterte Völker zählen nie als überlebt | AT + MP | | E2E-Tests | Grün | 23 rot (1 Produktfehler, 3 veraltete Tests) | – | behoben | AT | ## Prüfungen (letzter Lauf) - Backend: Typecheck, ESLint, 550 Unit-Tests, 211 E2E-Tests (isolierte Postgres 17), `nest build` – grün. - Frontend: Typecheck, ESLint, 48 Tests, `next build` – grün. Design-Detektor (impeccable): 0 Befunde. - Wabenhain: Typecheck, ESLint (geänderte Dateien), 538 Tests – grün. - Manuell (Browser, lokale Demo-Daten): Login, Heute-Ansicht Desktop/Mobil, Abhaken in Liste und „Als Nächstes“, Rückgängig (stellt vorherigen Status her), Profil-Bewegung „Aus“ wirkt, Aufgabenliste mobil. ## Zuständigkeit (Quelle der Wahrheit) ApiCore und Wabenhain bleiben getrennte Pakete; Verbindung nur über die HTTP-API von ApiCore. | Information | Maßgeblich | Sync-Verhalten (Wabenhain) | |---|---|---| | Stand, Völker, Durchsichten, Behandlungen, interne Notizen | ApiCore | wird nicht übertragen | | Ernte: Datum, Jahr | ApiCore | bei jedem Lauf aktualisiert | | Ernte: Sorte, Ort, Text | Wabenhain | nur Vorbelegung unbearbeiteter Entwürfe | | Charge: Losnummer, Abfülldatum, MHD, abgefüllte Gläser | ApiCore | bei jedem Lauf aktualisiert (Zuordnung über ApiCore-ID) | | Charge: Shop-Bestand, Bilder, Ernte-Zuordnung | Wabenhain | nie geändert; Bestand > Abfüllmenge wird gemeldet | | Produkt: Name, Text, Gewicht, Preis, Bilder, Sichtbarkeit | Wabenhain | nur beim ersten Anlegen vorbelegt | | Bestellungen, Kunden, Bewertungen | Wabenhain | nie Richtung ApiCore | Umgesetzt in Wabenhain `d38f430` (AT: Entscheidungslogik; CR: DB-Ablauf der Server-Action). ## Nächste Schritte 1. ~~Zuständigkeit pro Feld im Sync~~ – erledigt. 2. ~~Eigene, versionierte Sync-Schnittstelle~~ – erledigt: `GET /api/v1/shop-sync` (Vertragsversion 1, Feld-Allowlist, Berechtigung `shop-sync:read`). Bewusst ohne „geändert seit“: kleine Datenmenge, Voll-Abgleich heilt verpasste Läufe selbst. Wabenhain nutzt sie seit `5642f0c`; seit `3bc0693` ausschließlich (kein Rückfall auf andere Endpunkte). 3. ~~Nur-Lese-API-Schlüssel~~ – erledigt: Verwaltung → Automatisierung → API-Schlüssel. Nur Hash gespeichert, Klartext einmalig angezeigt, Scope nur `shop-sync:read`, widerrufbar, optional befristet, „zuletzt benutzt“ sichtbar. Wabenhain liest nur noch `APICORE_API_KEY`; der Passwort-Login ist entfernt (`3bc0693`). 4. Ende-zu-Ende-Test beider Systeme gegen Test-Instanzen (offen, Issue #39). 5. ~~Merge/Push~~ – erledigt. ## Offen / Entscheidungen 1. **Wabenhain-Sync gegen echtes ApiCore** nicht Ende zu Ende getestet (keine Zugangsdaten/Testinstanz verbunden). Mapping, Client und Schutzlogik sind unit-getestet bzw. per Code geprüft. 2. ~~Service-Konto statt API-Schlüssel~~ – erledigt: nur noch API-Schlüssel mit Scope `shop-sync:read`. 3. **Status „blockiert“** existiert im Aufgabenmodell nicht. Nicht ergänzt (Schemaänderung + fachliche Definition nötig). 4. **Produktbeschreibung** aus ApiCore wird in Wabenhain übernommen und auf der öffentlichen Herkunftsseite gezeigt – gilt damit als öffentlich. Falls sie intern gemeint ist: klären. 5. Automatische Vorschläge haben standardmäßig **keinen Termin** (keine erfundene Frist); je Regel über `config.dueInDays` einstellbar. 6. ~~Übrige Formulare/Seiten nicht überarbeitet~~ – erledigt im Oktober 2026, siehe „AuDHD-Anpassung aller Seiten“. ## Bestandsbuch (Tierarzneimittel) Grundlage: Art. 108 VO (EU) 2019/6, abgeglichen mit dem Muster-Formblatt des Veterinäramts Kassel (Stand März 2026) und der FAQ des LGL Bayern. Gilt für jedes Tierarzneimittel, auch freiverkäufliche Säuren; Aufbewahrung 5 Jahre. - Export: Stand → „Bestandsbuch {Jahr}“ (`/export/treatment-log.pdf?year=&apiaryId=`), ein Blatt je Stand. - Gleiche Behandlungen mehrerer Völker (Mittel, Charge, Menge, Zeitraum, Bezug) stehen in einer Zeile. - Standort am Behandlungstag: seit dieser Version mit der Behandlung gespeichert (`Treatment.apiaryId`). Für ältere Einträge aus der Umsetz-Historie abgeleitet; ist das unsicher, markiert `*` die Zeile. - Fehlende Pflichtangaben erscheinen als „FEHLT“ statt leer – das Blatt sieht nie vollständiger aus, als es ist. - Name und Anschrift des Halters: Verwaltung → Automatisierung → Stammdaten & Web. Erscheinen nur im Bestandsbuch; die öffentliche Chargen-Seite gibt sie nachweislich nicht aus (Test in `public.service.spec.ts`). ## Überwinterung (Ein-/Auswinterung & Verluste) - Seite „Überwinterung“ (Betrieb): je Winter (2026 = Winter 2026/27) eine Einwinterung und eine Auswinterung pro Volk. „Als Nächstes“ nennt die noch nicht eingewinterten Völker; „Auswintern“ erscheint erst nach der Einwinterung oder ab Januar. - Verlustursachen: die drei Kategorien des COLOSS-Monitorings (tot/leer, unlösbares Königinnenproblem, Naturereignis). Keine Diagnose-Liste – vermutete Gründe gehören in die Notiz. - Verlustrate = verloren / eingewintert (wie COLOSS). Solange Völker nicht ausgewintert sind, heißt sie „vorläufig“; ohne jede Auswinterung wird „–“ statt „0 %“ angezeigt. - „Volk als abgestorben kennzeichnen“ setzt den Status nur auf ausdrücklichen Wunsch (Häkchen, vorbelegt), in derselben Transaktion und mit Audit-Eintrag. - Rechte `winter-checks:read/create/update`: per Migration an Imker und Mitarbeiter wie Durchsichten, Nur-Lesen-Rollen nur lesen. Einträge sind im Backup enthalten. - Quelle Verlustkategorien: COLOSS-Monitoring (z. B. Gray et al. 2020, J. Apic. Res., doi:10.1080/00218839.2020.1797272). ## API-Sicherheit (Oktober 2026) Audit des gesamten Backends nach OWASP API Top 10 / ASVS L2 (25 Befunde), alle umgesetzt außer den zwei in ARCHITECTURE.md §9 begründeten Restpunkten. Verifikation: AT (Unit + E2E gegen Postgres 17). | Befund | Schwere | Commit | |---|---|---| | MFA-Zwischen-Token galt als Access-Token (MFA-Bypass) | Kritisch | `e0bd7b2` | | `users:update` konnte Admin-Konten übernehmen | Hoch | `c4a11b4` | | SSO-Konto über unverifizierten E-Mail-Claim gewählt | Hoch | `a25265b` | | Backup-Import überschreibt Benutzer; Export enthält Hashes/Secrets; Pfad-Traversal im Import | Hoch | `bd592ad` | | Backup-Pfad/Zeitplan frei wählbar | Mittel | `ebeb499` | | Webhooks: DNS-Rebinding, kein Timeout, Signatur ohne Zeitstempel | Mittel | `79d20ee` | | TOTP-Replay, keine Kontosperre, MFA-Aus nur mit Passwort, Timing-Enumeration | Mittel | `5c666fe` | | Refresh-Rotation nicht atomar, keine Höchstdauer | Mittel | `2de93bc` | | Throttler nach Auth, Proxy-Vertrauen fest, Cache/CSP, Logs mit Query | Mittel | `9ae1d96` | | API-Schlüssel ohne IP-Bindung, Scope über eigene Rechte hinaus | Mittel | `f1dc0de` | | CSV-Formel-Injection | Mittel | `61e3247` | | Passwortregel 8 Zeichen, kein Leak-Abgleich | Mittel | `0ba8996` | | Suche umgeht Leserechte | Niedrig–Mittel | `eafe925` | | Öffentliche Chargen-Seite, SSO-Callback-URL, Logout-Limit, 4xx-Mapping | Niedrig | `0899576` | | SSO-/MFA-Token mehrfach nutzbar; Rechte-Cache je Replika | Niedrig–Mittel | `45efb08` | Neue Umgebungsvariablen: `BACKUP_ENCRYPTION_KEY`, `BACKUP_ROOT`, `SESSION_MAX_AGE`, `TRUST_PROXY`, `PASSWORD_BREACH_CHECK` (siehe `.env.example`). Betrieb nach dem Update: Alle Benutzer melden sich einmal neu an (Access-Tokens ohne Audience gelten nicht mehr). Webhook-Empfänger prüfen die neue Signatur (`X-ApiCore-Timestamp`.`X-ApiCore-Delivery`.Body). Im Wabenhain-Schlüssel die Server-IP als erlaubte Adresse eintragen. ## AuDHD-Anpassung aller Seiten (Oktober 2026) Ziel: weniger gleichzeitig zu merken, keine Sackgassen, ein klarer nächster Schritt, handschuhtauglich. Verifikation: AT (111 Frontend-Tests, je Formular eine Spec) + CR; MP (Browser) für die neuen Seiten noch offen. Gemeinsame Bausteine (`src/components/form/*`, `query-state.tsx`, `detail-header.tsx`, `empty-state.tsx`): | Regel | Umsetzung | |---|---| | Was ist Pflicht? | `Field` zeigt „Pflicht“/„optional“, Fehler direkt am Feld, Fokus aufs erste Problem | | Nicht zu viel auf einmal | `FormSection`: nummerierte Schritte, seltene Felder eingeklappt mit Anzahl | | Unterbrechung kostet nichts | `useFormDraft`: Entwurf je Benutzer auf dem Gerät, wird ausdrücklich angeboten, beim Abmelden gelöscht | | Antippen statt Tippen | `RatingScale` (1–5 als Tasten mit benannten Enden), `ToggleChip` (Ja/Nein), Auswahl-Tasten statt Freitext | | Keine Sackgasse | `QueryState`: Laden → Fehler mit „Erneut versuchen“ → leer mit nächstem Schritt → Daten | | Ein Hauptschritt | `DetailHeader`: Überschrift, Zurück, Status als Symbol + Wort, eine Hauptaktion, Rest unter „Mehr“ | | Handschuhe | Felder 44 px, Tasten ≥ 40 px auf dem Handy, Speichern bleibt in Daumenreichweite, Zifferntastatur bei Zahlen | | Bewegung | Verschieben/Zoomen nur bei Bewegungsstufe „Voll“; Dialoge scrollen im Bildschirm | | Riskantes | Rückfrage mit Klartext (Vereinigen, Zähler, Vorlagen, Zuordnungen), Rückgängig beim Etiketten-Designer | Seiten: alle 14 Erfassungsformulare, Listen, Detailseiten (Volk, Stand, Königin, Honigcharge, Ernte, Schleuderung, Abfüllung, Ausrüstung), Bearbeiten-Dialoge am Volk, Kalender (Tagesliste auf dem Handy), Statistik, Überwinterung, Lager, Suche, Verwaltung (Benutzer, Audit-Log, Automatisierung, Backup). Nebenbei behoben: Fütterungsstatistik addierte Liter und Kilogramm (jetzt je Einheit), Kalender ordnete späte Einträge dem Folgetag zu (UTC), Zeichenfarbe der Königin wurde aus der ersten Ziffer geraten. Offen: Prüfung im Browser (Desktop + Handy) der neuen Seiten; Bearbeiten-Dialoge außerhalb des Volks (Honigcharge, Ernte, Abfüllung, Produkt) haben Zifferntastatur, aber noch nicht das volle Formular-Kit.