Vereins-Stammdaten-Kanal¶
Zweck. Der Vereinskatalog des Admin-Portals soll die Vereine zeigen, die eine angeschlossene Box in ihrer Vereinssoftware kennt — auch dann, wenn dort noch niemand eingewilligt hat.
Warum es den Kanal braucht¶
Der Katalog leitete „importierte" Vereine bisher allein aus
ClubDataScoreSheet.vereinsId ab. Dieser Kanal ist am Connector
consent-gatet (ConsentGate.java): Ohne GRANTED-Einwilligung verlässt keine
Scheibenzeile den Verein. Folge: Eine frisch angeschlossene Box liefert NULL
Zeilen und taucht im Katalog gar nicht auf — obwohl sie die Vereine ihres
Kreises längst kennt. Genau die Vereine, die man einladen möchte, waren damit
unsichtbar.
Am 06.09.2026 lagen im Spiegel von club-413067 107 Vereine in
SMDB.Verein (58 davon mit Scheibendaten), während die Prod-Datenbank genau
einen kannte.
Das Consent-Gate ist dabei nicht die richtige Stellschraube: SMDB.Verein
wird überhaupt nicht übertragen — es gibt kein Verein-Topic, keinen Router,
keinen Konsumenten. Ein gelockertes Gate ließe Leistungsdaten ohne
Einwilligung in die Cloud und brächte trotzdem keinen einzigen zusätzlichen
Verein.
Datenschutz¶
Übertragen werden ausschließlich Vereinsnummer und Vereinsname — also
Organisationsdaten. Wer dort schießt und wie, bleibt vollständig
consent-gated in ClubDataScoreSheet / -Serie / -Hit.
Dieselbe Begründung trägt schon den Starterlisten-Stammdaten-Kanal
(docs/install/starterlist-master-channel-spec.md): Ein Header ohne
Personenbezug braucht keine Einwilligung. Im Löschregister ist
AdminAuditEvent.clubDisplayName bereits als „Vereinsname, kein
Personenbezug" geführt.
Randfall wie beim Starterlisten-Kanal: Ein Vereinsname, der aus einem einzelnen Personennamen besteht, wäre ein Organisationsdatum mit Personenbezug. Für Schützenvereine ist das praktisch ausgeschlossen; falls es auftritt, gilt dieselbe Empfehlung wie dort (Whitelist oder Hashing).
Löschweg¶
Anders als der Starterlisten-Kanal hat dieser Kanal von Anfang an einen — das war die Bedingung für seine Einführung.
Der Push ist eine vollständige Meldung des Kenntnisstands, kein Nachtrag:
- Was gemeldet wird, wird aufgefrischt (
lastSeenAt,sourceDeleted = false). - Was fehlt, wird im selben Vorgang auf
sourceDeleted = truegesetzt. - Eine leere Meldung räumt den gesamten Kenntnisstand dieser Box ab. Das ist der Griff, mit dem ein abgebauter Anschluss aus dem Katalog verschwindet.
- Der Schlüssel ist
(reportedByClubId, vereinsId). Zeilen eines einzelnen Anschlusses lassen sich damit gezielt entfernen:DELETE FROM "ClubDataKnownClub" WHERE "reportedByClubId" = '<clubId>';
Der Ersatz läuft in EINER Transaktion mit einem Riegel je meldender Box
(pg_advisory_xact_lock, dasselbe Muster wie services/keygenSyncLock.ts).
Zwei Gründe:
- Ohne Transaktion hinterlässt ein Fehler nach dem dritten von hundert Vereinen einen halb ersetzten Stand.
- Ohne Riegel laufen zwei überlappende Meldungen derselben Box in eine
Verklemmung: A hält Verein 101 und will 102, B hält 102 und will 101.
PostgreSQL erschlägt eine der beiden Transaktionen (
40P01), und die betroffene Meldung scheitert mit einem 500, obwohl an ihr nichts falsch war. Nachgestellt inbackend/test/clubMasterChannel.db.test.ts.
Scharfschalten¶
Zwei Schritte, in dieser Reihenfolge:
- Backend-Release, das
POST /api/edge/clubs/synckennt. Vorher antwortet die Cloud 404, und der Connector meldet genau das als Grund. - Auf der Box (in
.envnebenTARGETSHOT_EDGE_API_TOKEN):
TARGETSHOT_CLUB_MASTER_SYNC_ENABLED=true
Per Vorgabe steht der Schalter auf false. Er ist die bewusste Freigabe
dafür, dass Stammdaten dritter Vereine das Haus verlassen — diese
Entscheidung gehört dem Betreiber, nicht einem Rollout.
Takt: TARGETSHOT_CLUB_MASTER_SYNC_SECONDS (Vorgabe 21600 = 6 h; Stammdaten
ändern sich selten). Ein Lauf lässt sich in der Box-Oberfläche über
POST /api/club-master-sync/run anstoßen, der Stand steht unter
GET /api/club-master-sync/status.
Wirkung im Portal¶
Ein so gemeldeter Verein erscheint im Vereinskatalog als imported_only —
genau wie ein Verein, der über Scheibendaten bekannt wurde. Der Scheiben-Kanal
bleibt der stärkere Beleg („hier wurde wirklich geschossen") und wird nicht
überschrieben; die Stammdaten-Meldung ergänzt nur, was er nicht sehen kann.
„Aufnehmen" (Bootstrap) findet den Verein in beiden Quellen. Ohne diesen zweiten Zweig stünde ein Verein zwar im Katalog, liefe beim Aufnehmen aber in einen 404 — sichtbar, aber nicht einladbar.