This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/docs-site/architecture/sdk-protection/index.html
BreakPilot Dev 557305db5d
Some checks failed
ci/woodpecker/push/integration Pipeline failed
ci/woodpecker/push/main Pipeline failed
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
feat: Add Academy, Whistleblower, Incidents SDK modules, pitch-deck, blog and CI/CD config
- Academy, Whistleblower, Incidents frontend pages with API proxies and types
- Vendor compliance API proxy route
- Go backend handlers and models for all new SDK modules
- Investor pitch-deck app with interactive slides
- Blog section with DSGVO, AI Act, NIS2, glossary articles
- MkDocs documentation site
- CI/CD pipelines (Woodpecker, GitHub Actions), security scanning config
- Planning and implementation documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:12:16 +01:00

3087 lines
71 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="canonical" href="https://macmini:8008/architecture/sdk-protection/">
<link rel="prev" href="../devsecops/">
<link rel="next" href="../environments/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>SDK Protection - Breakpilot Dokumentation</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="teal" data-md-color-accent="indigo">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#sdk-protection-middleware" class="md-skip">
Zum Inhalt
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Kopfzeile">
<a href="../.." title="Breakpilot Dokumentation" class="md-header__button md-logo" aria-label="Breakpilot Dokumentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Breakpilot Dokumentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
SDK Protection
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="teal" data-md-color-accent="indigo" aria-label="Dark Mode aktivieren" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Dark Mode aktivieren" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="teal" data-md-color-accent="indigo" aria-label="Light Mode aktivieren" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Light Mode aktivieren" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Suche" placeholder="Suche" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Suche">
<button type="reset" class="md-search__icon md-icon" title="Zurücksetzen" aria-label="Zurücksetzen" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Suche wird initialisiert
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Hauptnavigation" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../.." class="md-tabs__link">
Start
</a>
</li>
<li class="md-tabs__item">
<a href="../../getting-started/environment-setup/" class="md-tabs__link">
Erste Schritte
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../system-architecture/" class="md-tabs__link">
Architektur
</a>
</li>
<li class="md-tabs__item">
<a href="../../services/ki-daten-pipeline/" class="md-tabs__link">
Services
</a>
</li>
<li class="md-tabs__item">
<a href="../../api/backend-api/" class="md-tabs__link">
API
</a>
</li>
<li class="md-tabs__item">
<a href="../../development/testing/" class="md-tabs__link">
Entwicklung
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="Breakpilot Dokumentation" class="md-nav__button md-logo" aria-label="Breakpilot Dokumentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
Breakpilot Dokumentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Start
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2" >
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
<span class="md-ellipsis">
Erste Schritte
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Erste Schritte
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../getting-started/environment-setup/" class="md-nav__link">
<span class="md-ellipsis">
Umgebung einrichten
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../getting-started/mac-mini-setup/" class="md-nav__link">
<span class="md-ellipsis">
Mac Mini Setup
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" checked>
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="">
<span class="md-ellipsis">
Architektur
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Architektur
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../system-architecture/" class="md-nav__link">
<span class="md-ellipsis">
Systemuebersicht
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../auth-system/" class="md-nav__link">
<span class="md-ellipsis">
Auth-System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../mail-rbac-architecture/" class="md-nav__link">
<span class="md-ellipsis">
Mail-RBAC
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../multi-agent/" class="md-nav__link">
<span class="md-ellipsis">
Multi-Agent
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../secrets-management/" class="md-nav__link">
<span class="md-ellipsis">
Secrets Management
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../devsecops/" class="md-nav__link">
<span class="md-ellipsis">
DevSecOps
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
SDK Protection
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
SDK Protection
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Inhaltsverzeichnis">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Inhaltsverzeichnis
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1-worum-geht-es" class="md-nav__link">
<span class="md-ellipsis">
1. Worum geht es?
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-wie-funktioniert-der-schutz" class="md-nav__link">
<span class="md-ellipsis">
2. Wie funktioniert der Schutz?
</span>
</a>
<nav class="md-nav" aria-label="2. Wie funktioniert der Schutz?">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#score-zerfall" class="md-nav__link">
<span class="md-ellipsis">
Score-Zerfall
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#3-was-wird-erkannt" class="md-nav__link">
<span class="md-ellipsis">
3. Was wird erkannt?
</span>
</a>
<nav class="md-nav" aria-label="3. Was wird erkannt?">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#31-hohe-kategorie-diversitaet" class="md-nav__link">
<span class="md-ellipsis">
3.1 Hohe Kategorie-Diversitaet
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#32-burst-erkennung" class="md-nav__link">
<span class="md-ellipsis">
3.2 Burst-Erkennung
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#33-sequentielle-enumeration" class="md-nav__link">
<span class="md-ellipsis">
3.3 Sequentielle Enumeration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#34-ungewoehnliche-uhrzeiten" class="md-nav__link">
<span class="md-ellipsis">
3.4 Ungewoehnliche Uhrzeiten
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#35-multi-tenant-zugriff" class="md-nav__link">
<span class="md-ellipsis">
3.5 Multi-Tenant-Zugriff
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#4-quota-system-mengenbegrenzung" class="md-nav__link">
<span class="md-ellipsis">
4. Quota-System (Mengenbegrenzung)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-architektur" class="md-nav__link">
<span class="md-ellipsis">
5. Architektur
</span>
</a>
<nav class="md-nav" aria-label="5. Architektur">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#datenfluss-eines-sdk-requests" class="md-nav__link">
<span class="md-ellipsis">
Datenfluss eines SDK-Requests
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#valkey-datenstrukturen" class="md-nav__link">
<span class="md-ellipsis">
Valkey-Datenstrukturen
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#watermarking" class="md-nav__link">
<span class="md-ellipsis">
Watermarking
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#6-geschuetzte-endpunkte" class="md-nav__link">
<span class="md-ellipsis">
6. Geschuetzte Endpunkte
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#7-admin-verwaltung" class="md-nav__link">
<span class="md-ellipsis">
7. Admin-Verwaltung
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#8-dateien-und-quellcode" class="md-nav__link">
<span class="md-ellipsis">
8. Dateien und Quellcode
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#9-datenbank-tabellen" class="md-nav__link">
<span class="md-ellipsis">
9. Datenbank-Tabellen
</span>
</a>
<nav class="md-nav" aria-label="9. Datenbank-Tabellen">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#sdk_anomaly_scores" class="md-nav__link">
<span class="md-ellipsis">
sdk_anomaly_scores
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#sdk_protection_tiers" class="md-nav__link">
<span class="md-ellipsis">
sdk_protection_tiers
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#10-konfiguration" class="md-nav__link">
<span class="md-ellipsis">
10. Konfiguration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#11-tests" class="md-nav__link">
<span class="md-ellipsis">
11. Tests
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../environments/" class="md-nav__link">
<span class="md-ellipsis">
Environments
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../zeugnis-system/" class="md-nav__link">
<span class="md-ellipsis">
Zeugnis-System
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Services
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Services
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_1" >
<label class="md-nav__link" for="__nav_4_1" id="__nav_4_1_label" tabindex="0">
<span class="md-ellipsis">
KI-Daten-Pipeline
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_1">
<span class="md-nav__icon md-icon"></span>
KI-Daten-Pipeline
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../services/ki-daten-pipeline/" class="md-nav__link">
<span class="md-ellipsis">
Uebersicht
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/ki-daten-pipeline/architecture/" class="md-nav__link">
<span class="md-ellipsis">
Architektur
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_2" >
<label class="md-nav__link" for="__nav_4_2" id="__nav_4_2_label" tabindex="0">
<span class="md-ellipsis">
Klausur-Service
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_2">
<span class="md-nav__icon md-icon"></span>
Klausur-Service
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../services/klausur-service/" class="md-nav__link">
<span class="md-ellipsis">
Uebersicht
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/byoeh-system-erklaerung/" class="md-nav__link">
<span class="md-ellipsis">
BYOEH Systemerklaerung
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/BYOEH-Architecture/" class="md-nav__link">
<span class="md-ellipsis">
BYOEH Architektur
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/BYOEH-Developer-Guide/" class="md-nav__link">
<span class="md-ellipsis">
BYOEH Developer Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/NiBiS-Ingestion-Pipeline/" class="md-nav__link">
<span class="md-ellipsis">
NiBiS Pipeline
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/OCR-Labeling-Spec/" class="md-nav__link">
<span class="md-ellipsis">
OCR Labeling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/OCR-Compare/" class="md-nav__link">
<span class="md-ellipsis">
OCR Compare
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/RAG-Admin-Spec/" class="md-nav__link">
<span class="md-ellipsis">
RAG Admin
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/klausur-service/Worksheet-Editor-Architecture/" class="md-nav__link">
<span class="md-ellipsis">
Worksheet Editor
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../services/voice-service/" class="md-nav__link">
<span class="md-ellipsis">
Voice-Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/agent-core/" class="md-nav__link">
<span class="md-ellipsis">
Agent-Core
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4_5" >
<label class="md-nav__link" for="__nav_4_5" id="__nav_4_5_label" tabindex="0">
<span class="md-ellipsis">
AI-Compliance-SDK
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_5">
<span class="md-nav__icon md-icon"></span>
AI-Compliance-SDK
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../services/ai-compliance-sdk/" class="md-nav__link">
<span class="md-ellipsis">
Uebersicht
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/ai-compliance-sdk/ARCHITECTURE/" class="md-nav__link">
<span class="md-ellipsis">
Architektur
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/ai-compliance-sdk/DEVELOPER/" class="md-nav__link">
<span class="md-ellipsis">
Developer Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/ai-compliance-sdk/AUDITOR_DOCUMENTATION/" class="md-nav__link">
<span class="md-ellipsis">
Auditor Dokumentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../services/ai-compliance-sdk/SBOM/" class="md-nav__link">
<span class="md-ellipsis">
SBOM
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
API
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
API
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../api/backend-api/" class="md-nav__link">
<span class="md-ellipsis">
Backend API
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_6" >
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
<span class="md-ellipsis">
Entwicklung
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
Entwicklung
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../development/testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/documentation/" class="md-nav__link">
<span class="md-ellipsis">
Dokumentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/ci-cd-pipeline/" class="md-nav__link">
<span class="md-ellipsis">
CI/CD Pipeline
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Inhaltsverzeichnis">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Inhaltsverzeichnis
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1-worum-geht-es" class="md-nav__link">
<span class="md-ellipsis">
1. Worum geht es?
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-wie-funktioniert-der-schutz" class="md-nav__link">
<span class="md-ellipsis">
2. Wie funktioniert der Schutz?
</span>
</a>
<nav class="md-nav" aria-label="2. Wie funktioniert der Schutz?">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#score-zerfall" class="md-nav__link">
<span class="md-ellipsis">
Score-Zerfall
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#3-was-wird-erkannt" class="md-nav__link">
<span class="md-ellipsis">
3. Was wird erkannt?
</span>
</a>
<nav class="md-nav" aria-label="3. Was wird erkannt?">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#31-hohe-kategorie-diversitaet" class="md-nav__link">
<span class="md-ellipsis">
3.1 Hohe Kategorie-Diversitaet
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#32-burst-erkennung" class="md-nav__link">
<span class="md-ellipsis">
3.2 Burst-Erkennung
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#33-sequentielle-enumeration" class="md-nav__link">
<span class="md-ellipsis">
3.3 Sequentielle Enumeration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#34-ungewoehnliche-uhrzeiten" class="md-nav__link">
<span class="md-ellipsis">
3.4 Ungewoehnliche Uhrzeiten
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#35-multi-tenant-zugriff" class="md-nav__link">
<span class="md-ellipsis">
3.5 Multi-Tenant-Zugriff
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#4-quota-system-mengenbegrenzung" class="md-nav__link">
<span class="md-ellipsis">
4. Quota-System (Mengenbegrenzung)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-architektur" class="md-nav__link">
<span class="md-ellipsis">
5. Architektur
</span>
</a>
<nav class="md-nav" aria-label="5. Architektur">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#datenfluss-eines-sdk-requests" class="md-nav__link">
<span class="md-ellipsis">
Datenfluss eines SDK-Requests
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#valkey-datenstrukturen" class="md-nav__link">
<span class="md-ellipsis">
Valkey-Datenstrukturen
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#watermarking" class="md-nav__link">
<span class="md-ellipsis">
Watermarking
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#6-geschuetzte-endpunkte" class="md-nav__link">
<span class="md-ellipsis">
6. Geschuetzte Endpunkte
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#7-admin-verwaltung" class="md-nav__link">
<span class="md-ellipsis">
7. Admin-Verwaltung
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#8-dateien-und-quellcode" class="md-nav__link">
<span class="md-ellipsis">
8. Dateien und Quellcode
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#9-datenbank-tabellen" class="md-nav__link">
<span class="md-ellipsis">
9. Datenbank-Tabellen
</span>
</a>
<nav class="md-nav" aria-label="9. Datenbank-Tabellen">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#sdk_anomaly_scores" class="md-nav__link">
<span class="md-ellipsis">
sdk_anomaly_scores
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#sdk_protection_tiers" class="md-nav__link">
<span class="md-ellipsis">
sdk_protection_tiers
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#10-konfiguration" class="md-nav__link">
<span class="md-ellipsis">
10. Konfiguration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#11-tests" class="md-nav__link">
<span class="md-ellipsis">
11. Tests
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="sdk-protection-middleware">SDK Protection Middleware<a class="headerlink" href="#sdk-protection-middleware" title="Permanent link">&para;</a></h1>
<h2 id="1-worum-geht-es">1. Worum geht es?<a class="headerlink" href="#1-worum-geht-es" title="Permanent link">&para;</a></h2>
<p>Die SDK Protection Middleware schuetzt die Compliance-SDK-Endpunkte vor einer bestimmten Art von Angriff: der <strong>systematischen Enumeration</strong>. Was bedeutet das?</p>
<blockquote>
<p><em>Ein Wettbewerber registriert sich als zahlender Kunde und laesst ein Skript langsam und verteilt alle TOM-Controls, alle Pruefaspekte und alle Assessment-Kriterien abfragen. Aus den Ergebnissen rekonstruiert er die gesamte Compliance-Framework-Logik.</em></p>
</blockquote>
<p>Der klassische Rate Limiter (100 Requests/Minute) hilft hier nicht, weil ein cleverer Angreifer langsam vorgeht -- vielleicht nur 20 Anfragen pro Minute, dafuer systematisch und ueber Stunden. Die SDK Protection erkennt solche Muster und reagiert darauf.</p>
<div class="admonition info">
<p class="admonition-title">Kern-Designprinzip</p>
<p><strong>Normale Nutzer merken nichts.</strong> Ein Lehrer, der im TOM-Modul arbeitet, greift typischerweise auf 3-5 Kategorien zu und wiederholt Anfragen an gleiche Endpunkte. Ein Angreifer durchlaeuft dagegen 40+ Kategorien in alphabetischer Reihenfolge. Genau diesen Unterschied erkennt die Middleware.</p>
</div>
<hr />
<h2 id="2-wie-funktioniert-der-schutz">2. Wie funktioniert der Schutz?<a class="headerlink" href="#2-wie-funktioniert-der-schutz" title="Permanent link">&para;</a></h2>
<p>Die Middleware nutzt ein <strong>Anomaly-Score-System</strong>. Jeder Benutzer hat einen Score, der bei 0 beginnt. Verschiedene verdaechtige Verhaltensweisen erhoehen den Score. Ueber die Zeit sinkt er wieder ab. Je hoeher der Score, desto staerker wird der Benutzer gebremst.</p>
<p>Man kann es sich wie eine Ampel vorstellen:</p>
<table>
<thead>
<tr>
<th>Score</th>
<th>Ampel</th>
<th>Wirkung</th>
<th>Beispiel</th>
</tr>
</thead>
<tbody>
<tr>
<td>0-29</td>
<td>Gruen</td>
<td>Keine Einschraenkung</td>
<td>Normaler Nutzer</td>
</tr>
<tr>
<td>30-59</td>
<td>Gelb</td>
<td>1-3 Sekunden Verzoegerung</td>
<td>Leicht auffaelliges Muster</td>
</tr>
<tr>
<td>60-84</td>
<td>Orange</td>
<td>5-10 Sekunden Verzoegerung, reduzierte Details</td>
<td>Deutlich verdaechtiges Verhalten</td>
</tr>
<tr>
<td>85+</td>
<td>Rot</td>
<td>Zugriff blockiert (HTTP 429)</td>
<td>Sehr wahrscheinlich automatisierter Angriff</td>
</tr>
</tbody>
</table>
<h3 id="score-zerfall">Score-Zerfall<a class="headerlink" href="#score-zerfall" title="Permanent link">&para;</a></h3>
<p>Der Score sinkt automatisch: Alle 5 Minuten wird er mit dem Faktor 0,95 multipliziert. Ein Score von 60 faellt also innerhalb einer Stunde auf etwa 30 -- wenn kein neues verdaechtiges Verhalten hinzukommt.</p>
<hr />
<h2 id="3-was-wird-erkannt">3. Was wird erkannt?<a class="headerlink" href="#3-was-wird-erkannt" title="Permanent link">&para;</a></h2>
<p>Die Middleware erkennt fuenf verschiedene Anomalie-Muster:</p>
<h3 id="31-hohe-kategorie-diversitaet">3.1 Hohe Kategorie-Diversitaet<a class="headerlink" href="#31-hohe-kategorie-diversitaet" title="Permanent link">&para;</a></h3>
<p><strong>Was:</strong> Ein Benutzer greift innerhalb einer Stunde auf mehr als 40 verschiedene SDK-Kategorien zu.</p>
<p><strong>Warum verdaechtig:</strong> Ein normaler Nutzer arbeitet in der Regel mit 3-10 Kategorien. Wer systematisch alle durchlaeuft, sammelt vermutlich Daten.</p>
<p><strong>Score-Erhoehung:</strong> +15</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>Normal: tom/access-control → tom/access-control → tom/encryption → tom/encryption
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> (3 verschiedene Kategorien in einer Stunde)
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>Verdaechtig: tom/access-control → tom/encryption → tom/pseudonymization → tom/integrity
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> → tom/availability → tom/resilience → dsfa/threshold → dsfa/necessity → ...
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> (40+ verschiedene Kategorien in einer Stunde)
</code></pre></div>
<h3 id="32-burst-erkennung">3.2 Burst-Erkennung<a class="headerlink" href="#32-burst-erkennung" title="Permanent link">&para;</a></h3>
<p><strong>Was:</strong> Ein Benutzer sendet mehr als 15 Anfragen an die gleiche Kategorie innerhalb von 2 Minuten.</p>
<p><strong>Warum verdaechtig:</strong> Selbst ein eifriger Nutzer klickt nicht 15-mal pro Minute auf denselben Endpunkt. Das deutet auf automatisiertes Scraping hin.</p>
<p><strong>Score-Erhoehung:</strong> +20</p>
<h3 id="33-sequentielle-enumeration">3.3 Sequentielle Enumeration<a class="headerlink" href="#33-sequentielle-enumeration" title="Permanent link">&para;</a></h3>
<p><strong>Was:</strong> Die letzten 10 aufgerufenen Kategorien sind zu mindestens 70% in alphabetischer oder numerischer Reihenfolge.</p>
<p><strong>Warum verdaechtig:</strong> Menschen springen zwischen Kategorien -- sie arbeiten thematisch, nicht alphabetisch. Ein Skript dagegen iteriert oft ueber eine sortierte Liste.</p>
<p><strong>Score-Erhoehung:</strong> +25</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>Verdaechtig: assessment_general → compliance_general → controls_general
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a> → dsfa_measures → dsfa_necessity → dsfa_residual → dsfa_risks
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a> → dsfa_threshold → eh_general → namespace_general
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a> (alphabetisch sortiert = Skript-Verhalten)
</code></pre></div>
<h3 id="34-ungewoehnliche-uhrzeiten">3.4 Ungewoehnliche Uhrzeiten<a class="headerlink" href="#34-ungewoehnliche-uhrzeiten" title="Permanent link">&para;</a></h3>
<p><strong>Was:</strong> Anfragen zwischen 0:00 und 5:00 Uhr UTC.</p>
<p><strong>Warum verdaechtig:</strong> Lehrer arbeiten tagsüber. Wer um 3 Uhr morgens SDK-Endpunkte abfragt, ist wahrscheinlich ein automatisierter Prozess.</p>
<p><strong>Score-Erhoehung:</strong> +10</p>
<h3 id="35-multi-tenant-zugriff">3.5 Multi-Tenant-Zugriff<a class="headerlink" href="#35-multi-tenant-zugriff" title="Permanent link">&para;</a></h3>
<p><strong>Was:</strong> Ein Benutzer greift innerhalb einer Stunde auf mehr als 3 verschiedene Mandanten (Tenants) zu.</p>
<p><strong>Warum verdaechtig:</strong> Ein normaler Nutzer gehoert zu einem Mandanten. Wer mehrere durchprobiert, koennte versuchen, mandantenuebergreifend Daten zu sammeln.</p>
<p><strong>Score-Erhoehung:</strong> +15</p>
<hr />
<h2 id="4-quota-system-mengenbegrenzung">4. Quota-System (Mengenbegrenzung)<a class="headerlink" href="#4-quota-system-mengenbegrenzung" title="Permanent link">&para;</a></h2>
<p>Zusaetzlich zum Anomaly-Score gibt es klassische Mengenbegrenzungen in vier Zeitfenstern:</p>
<table>
<thead>
<tr>
<th>Tier</th>
<th>pro Minute</th>
<th>pro Stunde</th>
<th>pro Tag</th>
<th>pro Monat</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Free</strong></td>
<td>30</td>
<td>500</td>
<td>3.000</td>
<td>50.000</td>
</tr>
<tr>
<td><strong>Standard</strong></td>
<td>60</td>
<td>1.500</td>
<td>10.000</td>
<td>200.000</td>
</tr>
<tr>
<td><strong>Enterprise</strong></td>
<td>120</td>
<td>5.000</td>
<td>50.000</td>
<td>1.000.000</td>
</tr>
</tbody>
</table>
<p>Wenn ein Limit in irgendeinem Zeitfenster ueberschritten wird, erhaelt der Nutzer sofort HTTP 429 -- unabhaengig vom Anomaly-Score.</p>
<hr />
<h2 id="5-architektur">5. Architektur<a class="headerlink" href="#5-architektur" title="Permanent link">&para;</a></h2>
<h3 id="datenfluss-eines-sdk-requests">Datenfluss eines SDK-Requests<a class="headerlink" href="#datenfluss-eines-sdk-requests" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>Request kommt an
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a>│ Ist der Pfad geschuetzt? │
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a>│ (/api/sdk/*, /api/v1/tom/*, /api/v1/dsfa/*, ...) │
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a>│ Nein → direkt weiterleiten │
<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a>└──────────────┬──────────────────────────────────────────────┘
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a> │ Ja
<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a>
<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a>│ User + Tier + Kategorie extrahieren │
<a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a>│ (aus Session, API-Key oder X-SDK-Tier Header) │
<a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a>└──────────────┬──────────────────────────────────────────────┘
<a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a>
<a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a>
<a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a>│ Multi-Window Quota pruefen │
<a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a>│ (Minute / Stunde / Tag / Monat) │
<a id="__codelineno-2-20" name="__codelineno-2-20" href="#__codelineno-2-20"></a>│ Ueberschritten → HTTP 429 zurueck │
<a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a>└──────────────┬──────────────────────────────────────────────┘
<a id="__codelineno-2-22" name="__codelineno-2-22" href="#__codelineno-2-22"></a> │ OK
<a id="__codelineno-2-23" name="__codelineno-2-23" href="#__codelineno-2-23"></a>
<a id="__codelineno-2-24" name="__codelineno-2-24" href="#__codelineno-2-24"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-25" name="__codelineno-2-25" href="#__codelineno-2-25"></a>│ Anomaly-Score laden (aus Valkey) │
<a id="__codelineno-2-26" name="__codelineno-2-26" href="#__codelineno-2-26"></a>│ Zeitbasierten Zerfall anwenden (×0,95 alle 5 min) │
<a id="__codelineno-2-27" name="__codelineno-2-27" href="#__codelineno-2-27"></a>└──────────────┬──────────────────────────────────────────────┘
<a id="__codelineno-2-28" name="__codelineno-2-28" href="#__codelineno-2-28"></a>
<a id="__codelineno-2-29" name="__codelineno-2-29" href="#__codelineno-2-29"></a>
<a id="__codelineno-2-30" name="__codelineno-2-30" href="#__codelineno-2-30"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-31" name="__codelineno-2-31" href="#__codelineno-2-31"></a>│ Anomalie-Detektoren ausfuehren: │
<a id="__codelineno-2-32" name="__codelineno-2-32" href="#__codelineno-2-32"></a>│ ├── Diversity-Tracking (+15 wenn &gt;40 Kategorien/h) │
<a id="__codelineno-2-33" name="__codelineno-2-33" href="#__codelineno-2-33"></a>│ ├── Burst-Detection (+20 wenn &gt;15 gleiche/2min) │
<a id="__codelineno-2-34" name="__codelineno-2-34" href="#__codelineno-2-34"></a>│ ├── Sequential-Enumeration (+25 wenn sortiert) │
<a id="__codelineno-2-35" name="__codelineno-2-35" href="#__codelineno-2-35"></a>│ ├── Unusual-Hours (+10 wenn 0-5 Uhr UTC) │
<a id="__codelineno-2-36" name="__codelineno-2-36" href="#__codelineno-2-36"></a>│ └── Multi-Tenant (+15 wenn &gt;3 Tenants/h) │
<a id="__codelineno-2-37" name="__codelineno-2-37" href="#__codelineno-2-37"></a>└──────────────┬──────────────────────────────────────────────┘
<a id="__codelineno-2-38" name="__codelineno-2-38" href="#__codelineno-2-38"></a>
<a id="__codelineno-2-39" name="__codelineno-2-39" href="#__codelineno-2-39"></a>
<a id="__codelineno-2-40" name="__codelineno-2-40" href="#__codelineno-2-40"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-41" name="__codelineno-2-41" href="#__codelineno-2-41"></a>│ Throttle-Level bestimmen │
<a id="__codelineno-2-42" name="__codelineno-2-42" href="#__codelineno-2-42"></a>│ Level 3 (Score ≥85) → HTTP 429 │
<a id="__codelineno-2-43" name="__codelineno-2-43" href="#__codelineno-2-43"></a>│ Level 2 (Score ≥60) → 5-10s Delay + reduzierte Details │
<a id="__codelineno-2-44" name="__codelineno-2-44" href="#__codelineno-2-44"></a>│ Level 1 (Score ≥30) → 1-3s Delay │
<a id="__codelineno-2-45" name="__codelineno-2-45" href="#__codelineno-2-45"></a>│ Level 0 → keine Einschraenkung │
<a id="__codelineno-2-46" name="__codelineno-2-46" href="#__codelineno-2-46"></a>└──────────────┬──────────────────────────────────────────────┘
<a id="__codelineno-2-47" name="__codelineno-2-47" href="#__codelineno-2-47"></a>
<a id="__codelineno-2-48" name="__codelineno-2-48" href="#__codelineno-2-48"></a>
<a id="__codelineno-2-49" name="__codelineno-2-49" href="#__codelineno-2-49"></a>┌─────────────────────────────────────────────────────────────┐
<a id="__codelineno-2-50" name="__codelineno-2-50" href="#__codelineno-2-50"></a>│ Request weiterleiten │
<a id="__codelineno-2-51" name="__codelineno-2-51" href="#__codelineno-2-51"></a>│ Response-Headers setzen: │
<a id="__codelineno-2-52" name="__codelineno-2-52" href="#__codelineno-2-52"></a>│ ├── X-SDK-Quota-Remaining-Minute/Hour │
<a id="__codelineno-2-53" name="__codelineno-2-53" href="#__codelineno-2-53"></a>│ ├── X-SDK-Throttle-Level │
<a id="__codelineno-2-54" name="__codelineno-2-54" href="#__codelineno-2-54"></a>│ ├── X-SDK-Detail-Reduced (bei Level ≥2) │
<a id="__codelineno-2-55" name="__codelineno-2-55" href="#__codelineno-2-55"></a>│ └── X-BP-Trace (HMAC-Watermark) │
<a id="__codelineno-2-56" name="__codelineno-2-56" href="#__codelineno-2-56"></a>└─────────────────────────────────────────────────────────────┘
</code></pre></div>
<h3 id="valkey-datenstrukturen">Valkey-Datenstrukturen<a class="headerlink" href="#valkey-datenstrukturen" title="Permanent link">&para;</a></h3>
<p>Die Middleware speichert alle Tracking-Daten in Valkey (Redis-Fork). Wenn Valkey nicht erreichbar ist, wird automatisch auf eine In-Memory-Implementierung zurueckgefallen.</p>
<table>
<thead>
<tr>
<th>Zweck</th>
<th>Valkey-Typ</th>
<th>Key-Muster</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Quota pro Zeitfenster</td>
<td>Sorted Set</td>
<td><code>sdk_protect:quota:{user}:{window}</code></td>
<td>Fenster + 10s</td>
</tr>
<tr>
<td>Kategorie-Diversitaet</td>
<td>Set</td>
<td><code>sdk_protect:diversity:{user}:{stunde}</code></td>
<td>3660s</td>
</tr>
<tr>
<td>Burst-Tracking</td>
<td>Sorted Set</td>
<td><code>sdk_protect:burst:{user}:{kategorie}</code></td>
<td>130s</td>
</tr>
<tr>
<td>Sequenz-Tracking</td>
<td>List</td>
<td><code>sdk_protect:seq:{user}</code></td>
<td>310s</td>
</tr>
<tr>
<td>Anomaly-Score</td>
<td>Hash</td>
<td><code>sdk_protect:score:{user}</code></td>
<td>86400s</td>
</tr>
<tr>
<td>Tenant-Tracking</td>
<td>Set</td>
<td><code>sdk_protect:tenants:{user}:{stunde}</code></td>
<td>3660s</td>
</tr>
</tbody>
</table>
<h3 id="watermarking">Watermarking<a class="headerlink" href="#watermarking" title="Permanent link">&para;</a></h3>
<p>Jede Antwort enthaelt einen <code>X-BP-Trace</code> Header mit einem HMAC-basierten Fingerabdruck. Damit kann nachtraeglich nachgewiesen werden, welcher Benutzer wann welche Daten abgerufen hat -- ohne dass der Benutzer den Trace veraendern kann.</p>
<hr />
<h2 id="6-geschuetzte-endpunkte">6. Geschuetzte Endpunkte<a class="headerlink" href="#6-geschuetzte-endpunkte" title="Permanent link">&para;</a></h2>
<p>Die Middleware schuetzt alle Pfade, die SDK- und Compliance-relevante Daten liefern:</p>
<table>
<thead>
<tr>
<th>Pfad-Prefix</th>
<th>Bereich</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/api/sdk/*</code></td>
<td>SDK-Hauptendpunkte</td>
</tr>
<tr>
<td><code>/api/compliance/*</code></td>
<td>Compliance-Bewertungen</td>
</tr>
<tr>
<td><code>/api/v1/tom/*</code></td>
<td>Technisch-organisatorische Massnahmen</td>
</tr>
<tr>
<td><code>/api/v1/dsfa/*</code></td>
<td>Datenschutz-Folgenabschaetzung</td>
</tr>
<tr>
<td><code>/api/v1/vvt/*</code></td>
<td>Verarbeitungsverzeichnis</td>
</tr>
<tr>
<td><code>/api/v1/controls/*</code></td>
<td>Controls und Massnahmen</td>
</tr>
<tr>
<td><code>/api/v1/assessment/*</code></td>
<td>Assessment-Bewertungen</td>
</tr>
<tr>
<td><code>/api/v1/eh/*</code></td>
<td>Erwartungshorizonte</td>
</tr>
<tr>
<td><code>/api/v1/namespace/*</code></td>
<td>Namespace-Verwaltung</td>
</tr>
</tbody>
</table>
<p>Nicht geschuetzt sind <code>/health</code>, <code>/metrics</code> und <code>/api/health</code>.</p>
<hr />
<h2 id="7-admin-verwaltung">7. Admin-Verwaltung<a class="headerlink" href="#7-admin-verwaltung" title="Permanent link">&para;</a></h2>
<p>Ueber das Admin-Dashboard koennen Anomaly-Scores eingesehen und verwaltet werden:</p>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Methode</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/api/admin/middleware/sdk-protection/scores</code></td>
<td>GET</td>
<td>Aktuelle Anomaly-Scores aller Benutzer</td>
</tr>
<tr>
<td><code>/api/admin/middleware/sdk-protection/stats</code></td>
<td>GET</td>
<td>Statistik: Benutzer pro Throttle-Level</td>
</tr>
<tr>
<td><code>/api/admin/middleware/sdk-protection/reset-score/{user_id}</code></td>
<td>POST</td>
<td>Score eines Benutzers zuruecksetzen</td>
</tr>
<tr>
<td><code>/api/admin/middleware/sdk-protection/tiers</code></td>
<td>GET</td>
<td>Tier-Konfigurationen anzeigen</td>
</tr>
<tr>
<td><code>/api/admin/middleware/sdk-protection/tiers/{name}</code></td>
<td>PUT</td>
<td>Tier-Limits aendern</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="8-dateien-und-quellcode">8. Dateien und Quellcode<a class="headerlink" href="#8-dateien-und-quellcode" title="Permanent link">&para;</a></h2>
<table>
<thead>
<tr>
<th>Datei</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>backend/middleware/sdk_protection.py</code></td>
<td>Kern-Middleware (~460 Zeilen)</td>
</tr>
<tr>
<td><code>backend/middleware/__init__.py</code></td>
<td>Export der Middleware-Klassen</td>
</tr>
<tr>
<td><code>backend/main.py</code></td>
<td>Registrierung im FastAPI-Stack</td>
</tr>
<tr>
<td><code>backend/middleware_admin_api.py</code></td>
<td>Admin-API-Endpoints</td>
</tr>
<tr>
<td><code>backend/migrations/add_sdk_protection_tables.sql</code></td>
<td>Datenbank-Migration</td>
</tr>
<tr>
<td><code>backend/tests/test_middleware.py</code></td>
<td>14 Tests fuer alle Erkennungsmechanismen</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="9-datenbank-tabellen">9. Datenbank-Tabellen<a class="headerlink" href="#9-datenbank-tabellen" title="Permanent link">&para;</a></h2>
<h3 id="sdk_anomaly_scores">sdk_anomaly_scores<a class="headerlink" href="#sdk_anomaly_scores" title="Permanent link">&para;</a></h3>
<p>Speichert Snapshots der Anomaly-Scores fuer Audit und Analyse.</p>
<table>
<thead>
<tr>
<th>Spalte</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>UUID</td>
<td>Primaerschluessel</td>
</tr>
<tr>
<td><code>user_id</code></td>
<td>VARCHAR(255)</td>
<td>Benutzer-Identifikation</td>
</tr>
<tr>
<td><code>score</code></td>
<td>DECIMAL(5,2)</td>
<td>Aktueller Anomaly-Score</td>
</tr>
<tr>
<td><code>throttle_level</code></td>
<td>SMALLINT</td>
<td>Aktueller Throttle-Level (0-3)</td>
</tr>
<tr>
<td><code>triggered_rules</code></td>
<td>JSONB</td>
<td>Welche Regeln ausgeloest wurden</td>
</tr>
<tr>
<td><code>endpoint_diversity_count</code></td>
<td>INT</td>
<td>Anzahl verschiedener Kategorien</td>
</tr>
<tr>
<td><code>request_count_1h</code></td>
<td>INT</td>
<td>Anfragen in der letzten Stunde</td>
</tr>
<tr>
<td><code>snapshot_at</code></td>
<td>TIMESTAMPTZ</td>
<td>Zeitpunkt des Snapshots</td>
</tr>
</tbody>
</table>
<h3 id="sdk_protection_tiers">sdk_protection_tiers<a class="headerlink" href="#sdk_protection_tiers" title="Permanent link">&para;</a></h3>
<p>Konfigurierbare Quota-Tiers, editierbar ueber die Admin-API.</p>
<table>
<thead>
<tr>
<th>Spalte</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>tier_name</code></td>
<td>VARCHAR(50)</td>
<td>Name des Tiers (free, standard, enterprise)</td>
</tr>
<tr>
<td><code>quota_per_minute</code></td>
<td>INT</td>
<td>Maximale Anfragen pro Minute</td>
</tr>
<tr>
<td><code>quota_per_hour</code></td>
<td>INT</td>
<td>Maximale Anfragen pro Stunde</td>
</tr>
<tr>
<td><code>quota_per_day</code></td>
<td>INT</td>
<td>Maximale Anfragen pro Tag</td>
</tr>
<tr>
<td><code>quota_per_month</code></td>
<td>INT</td>
<td>Maximale Anfragen pro Monat</td>
</tr>
<tr>
<td><code>diversity_threshold</code></td>
<td>INT</td>
<td>Max verschiedene Kategorien pro Stunde</td>
</tr>
<tr>
<td><code>burst_threshold</code></td>
<td>INT</td>
<td>Max gleiche Kategorie in 2 Minuten</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="10-konfiguration">10. Konfiguration<a class="headerlink" href="#10-konfiguration" title="Permanent link">&para;</a></h2>
<p>Die Middleware wird in <code>main.py</code> registriert:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">middleware</span><span class="w"> </span><span class="kn">import</span> <span class="n">SDKProtectionMiddleware</span>
<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>
<a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="n">app</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">SDKProtectionMiddleware</span><span class="p">)</span>
</code></pre></div>
<p>Alle Parameter koennen ueber die <code>SDKProtectionConfig</code> Dataclass angepasst werden. Die wichtigsten Umgebungsvariablen:</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Default</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>VALKEY_URL</code></td>
<td><code>redis://localhost:6379</code></td>
<td>Verbindung zur Valkey-Instanz</td>
</tr>
<tr>
<td><code>SDK_WATERMARK_SECRET</code></td>
<td>(generiert)</td>
<td>HMAC-Secret fuer Watermarks</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="11-tests">11. Tests<a class="headerlink" href="#11-tests" title="Permanent link">&para;</a></h2>
<p>Die Middleware wird durch 14 automatisierte Tests abgedeckt:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="c1"># Alle SDK Protection Tests ausfuehren</span>
<a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>docker<span class="w"> </span>compose<span class="w"> </span>run<span class="w"> </span>--rm<span class="w"> </span>--no-deps<span class="w"> </span>backend<span class="w"> </span><span class="se">\</span>
<a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="w"> </span>python<span class="w"> </span>-m<span class="w"> </span>pytest<span class="w"> </span>tests/test_middleware.py<span class="w"> </span>-v<span class="w"> </span>-k<span class="w"> </span>sdk
</code></pre></div>
<table>
<thead>
<tr>
<th>Test</th>
<th>Prueft</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>test_allows_normal_request</code></td>
<td>Normaler Request wird durchgelassen</td>
</tr>
<tr>
<td><code>test_blocks_after_quota_exceeded</code></td>
<td>429 bei Quota-Ueberschreitung</td>
</tr>
<tr>
<td><code>test_diversity_tracking_increments_score</code></td>
<td>Viele Kategorien erhoehen den Score</td>
</tr>
<tr>
<td><code>test_burst_detection</code></td>
<td>Schnelle gleiche Anfragen erhoehen den Score</td>
</tr>
<tr>
<td><code>test_sequential_enumeration_detection</code></td>
<td>Alphabetische Muster werden erkannt</td>
</tr>
<tr>
<td><code>test_progressive_throttling_level_1</code></td>
<td>Delay bei Score &gt;= 30</td>
</tr>
<tr>
<td><code>test_progressive_throttling_level_3_blocks</code></td>
<td>Block bei Score &gt;= 85</td>
</tr>
<tr>
<td><code>test_score_decay_over_time</code></td>
<td>Score sinkt ueber die Zeit</td>
</tr>
<tr>
<td><code>test_skips_non_protected_paths</code></td>
<td>Nicht-SDK-Pfade bleiben frei</td>
</tr>
<tr>
<td><code>test_watermark_header_present</code></td>
<td>X-BP-Trace Header vorhanden</td>
</tr>
<tr>
<td><code>test_fallback_to_inmemory</code></td>
<td>Funktioniert ohne Valkey</td>
</tr>
<tr>
<td><code>test_no_user_passes_through</code></td>
<td>Anonyme Requests passieren</td>
</tr>
<tr>
<td><code>test_category_extraction</code></td>
<td>Korrekte Kategorie-Zuordnung</td>
</tr>
<tr>
<td><code>test_quota_headers_present</code></td>
<td>Response-Headers vorhanden</td>
</tr>
</tbody>
</table>
</article>
</div>
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Zurück zum Seitenanfang
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
<div class="md-social">
<a href="http://macmini:3003/breakpilot/breakpilot-pwa" target="_blank" rel="noopener" title="macmini:3003" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../..", "features": ["search.highlight", "search.suggest", "navigation.tabs", "navigation.sections", "navigation.expand", "navigation.top", "content.code.copy", "content.tabs.link", "toc.follow"], "search": "../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.79ae519e.min.js"></script>
</body>
</html>