**Zusatzpakete je API-Schlüssel**: `api_keys.features` hält die freigegebenen Pakete eines Schlüssels (`ApiKey::FEATURES`), gesetzt von Admin über `PATCH admin/api_keys/:id` mit `features: [...]`. Die Pakete werden individuell vereinbart; ein Zugang aus dem Antragsprozess startet immer ohne. Ein Schlüssel ohne Freigabe bekommt 403, eine Sitzung kommt wie bisher durch. Bisher lieferte `games/:id/detailed_stats` an jeden aktiven Schlüssel aus, jetzt nur noch mit `detailed_stats`. Bei der Migration behalten nur die in der Verwaltung von Hand angelegten Schlüssel (Frontend, Prerender, eigene Anwendungen) die Freigabe; alle Zugänge aus dem Antragsprozess verlieren sie.
**Schussstatistik einer Liga** (`GET leagues/:id/shot_stats`, Paket `league_shot_stats`): Schüsse, Tore und Gegenwerte je Mannschaft, Schüsse, Tore und Trefferquote je Spielerin/Spieler sowie Paraden und Fangquote je Torhüter über alle beendeten Spiele im Tabletmodus. Mannschaften und Personen tragen ihre ID, Namen in öffentlicher Anzeigeform. `games_ended`, `games_captured` und `games_complete` zeigen, wie viele Spiele der Liga die Zahlen abdecken. Die Website zeigt diese Statistik nicht.
Verbessert
**Anonyme Website-Sitzung statt API-Key im ausgelieferten Frontend**: Bisher schickte die Website einen festen API-Key mit, der im Bundle von saisonmanager.de im Klartext stand. Wer ihn herauskopierte, hatte einen ungedrosselten Zugang mit allen Zusatzpaketen und umging damit Antrag und Nutzungsvereinbarung. Neu holt sich der Browser über `POST public_session` eine anonyme Sitzung: ein signiertes, httponly Cookie (`SameSite=Strict`), 24 Stunden gültig und nach 12 Stunden still verlängert, ohne Datenbanktabelle. Öffentliche Endpunkte nehmen sie als dritten Weg neben Anmeldung und Key an; ohne beides antworten sie weiter mit 401 `API key required`, jetzt zusätzlich mit `code: public_session_required`, damit das Frontend die Sitzung holt statt abzumelden. Gedrosselt wird je Sitzung (300 Abrufe pro Minute) und beim Ausstellen je Adresse (300 pro Minute, damit auch eine volle Halle hinter einer IP beim Anpfiff zugleich an Sitzungen kommt, und 2000 pro Stunde gegen Skripte, die laufend neue Sitzungen holen; IPv6 je /64), nicht der laufende Abruf je Adresse, weil in der Halle alle Zuschauer eine IP teilen. Ein erfundenes `user_id`-Cookie hebelt den Sitzungstopf nicht aus. Zugriffe über die Sitzung zählt ein eigener Tageszähler je Endpunkt (`public_session_usages`, Auswertung unter `GET admin/api_keys/public_usage`), bisher standen sie in der Statistik des Frontend-Keys. Jeder Zugriff über Key oder Sitzung schreibt außerdem eine Logzeile `public-access api_key=<id>` bzw. `public_session=<Präfix>` mit IP und Endpunkt, damit sich zuordnen lässt, welcher Server mit welchem Key abruft. API-Keys funktionieren unverändert. Ein signiertes Cookie mit unerwartetem Inhalt wird nach Sentry gemeldet. **Übergang:** Erst die API, dann das Frontend ausrollen (ein neues Frontend gegen eine alte API meldet Besucher ab). Den Frontend-Key erst deaktivieren, wenn das neue Frontend einige Tage live ist, und **vorher** einen eigenen Prerender-Key ohne Rate-Limit anlegen und im Frontend-Checkout in `src/environments/.api-key` eintragen, denn dort steht bisher der Frontend-Key und der Prod-Build bräche sonst ab.
**Sprungmarken in der YouTube-Beschreibung nur noch für Spiele im Tabletmodus**: Ohne am Tablet erfassten Schuss ist die Eingabezeit des Tores der einzige Anker, und die liegt je nach Sekretariat 1,5 bis über 3 Minuten hinter dem Tor, bei Nachträgen noch weiter. Bei Spiel 58679 landeten so die meisten Marken erst hinter dem Tor. Spiele ohne Tabletmodus bekommen deshalb keine Sprungmarken mehr; ein schon geschriebener Block wird bei einem der nächsten Wächterläufe aus der Beschreibung entfernt (ältere Aufzeichnungen in der täglichen Nachrechnung), der übrige Text bleibt stehen. Damit das Entfernen das YouTube-Kontingent nicht aufbraucht und der Wächter weiter hängende Übertragungen beenden kann, werden höchstens 40 Blöcke je 24 Stunden entfernt, der Rest folgt an den Tagen danach. In Tablet-Spielen ändert sich nichts: Tore mit verknüpftem Schuss werden über den Schusszeitpunkt markiert, Tore ohne Schuss und Strafen weiter über die Eingabezeit.