docs: technische Dokumentation + ADRs

- docs/ARCHITEKTUR.md: Module, Datenmodell, Request-Fluss, RBAC-Matrix,
  ITIL-Abbildung, Sicherheitsarchitektur
- docs/BETRIEB.md: ENV-Referenz, Deployment-/Update-Prozedur (SRV1361746),
  Backup/Retention, Troubleshooting (inkl. Werkzeug-Hash-Diagnose,
  Rate-Limit-Entsperrung), Monitoring-Punkte
- docs/adr/: ADR-001..008 (Rust-Rewrite, Server-Sessions, Hash-Migration
  inkl. scrypt-Lehre, ITIL-Rollen/Change-Gate, idempotente Migrationen,
  Multi-Stage-Build, CSP, SSRF-Policy) + Index
- README: Verweis auf docs/ und Prozess-Doku in mscadm/AES projects/itsm (dev)
This commit is contained in:
Moe 2026-07-15 11:49:47 +02:00
parent f73239211c
commit 97facdce18
12 changed files with 360 additions and 0 deletions

View File

@ -45,6 +45,14 @@ werden beim ersten Login transparent auf Argon2id umgestellt.
- DSGVO-Retention: automatische Bereinigung von Tickets/Audit-Log gemaess - DSGVO-Retention: automatische Bereinigung von Tickets/Audit-Log gemaess
Mandanten-Fristen; Login-Versuche nach 7 Tagen, abgelaufene Sessions sofort. Mandanten-Fristen; Login-Versuche nach 7 Tagen, abgelaufene Sessions sofort.
## Dokumentation
- `docs/ARCHITEKTUR.md` — technische Architektur (Module, Datenmodell, Request-Fluss, RBAC-Matrix, ITIL-Abbildung)
- `docs/BETRIEB.md` — Betriebshandbuch (ENV, Deployment/Update, Backup, Troubleshooting)
- `docs/adr/` — Architecture Decision Records (ADR-001 bis ADR-008)
- Prozess-Doku nach AES-Regelwerk (Projektbeschreibung, Phasen-/Forensik-Reports, Tasks):
`mscadm/AES`, Branch `dev`, Verzeichnis `projects/itsm/`
## Betrieb ## Betrieb
# Entwicklung # Entwicklung

104
docs/ARCHITEKTUR.md Executable file
View File

@ -0,0 +1,104 @@
# ITSM — Technische Architektur
Stand: 2026-07-15 (Rust-Rewrite, Version 1.0). Verbindliche Entscheidungen: siehe `docs/adr/`.
## 1. Überblick
Mandantenfähige ITSM-Plattform (Tickets, Service-Katalog, Wissensdatenbank, CMDB)
als einzelnes Rust-Binary mit PostgreSQL. AES ist ein buchbarer Service im
Katalog (HTTP-Integration, kein Code-Import). Repo-Bearbeitung aus Tickets
läuft über die Gitea-kompatible Forge-Contents-API.
Browser ──HTTP──► itsm (axum, Port 8090) ──► PostgreSQL 16
├──► AES-Dashboard (Erreichbarkeits-Check, Katalog)
└──► Forge-API (Repo-Edit aus Change-Tickets)
## 2. Modulstruktur (src/)
| Modul | Verantwortung |
|---|---|
| `main.rs` | App-Aufbau, Routing, Retention-Hintergrundtask |
| `config.rs` | ENV-Konfiguration + Startvalidierung |
| `web.rs` | AppState, Middleware (Session, CSRF, Client-IP, Security-Header), Fehlertyp, RBAC-Helfer, PageCtx |
| `db.rs` | Datenzugriff (deadpool/tokio-postgres), alle Queries parametrisiert und tenant-gescoped |
| `security.rs` | Passwort-Hashing/-Verifikation (Argon2id + Werkzeug pbkdf2/scrypt), Token, Policy, SSRF-Guard |
| `itil.rs` | ITIL-Logik: Rollen, Statusmodell, Prioritätsmatrix, Change-Gate (reine Funktionen, unit-getestet) |
| `auth.rs` | Login (Rate-Limit, Rehash-Migration), Logout, Ersteinrichtung |
| `tickets.rs` | Listen/KPIs/Sparklines, Ticket-API, Statuswechsel, Change-Freigabe, Problem/CI-Links, Repo-Edit, Kanban |
| `services.rs` | Service-Katalog, paralleler Erreichbarkeits-Check (30 s Cache) |
| `kb.rs` | Wissensdatenbank inkl. Freigabe-Workflow |
| `cmdb.rs` | Configuration Items + Beziehungen |
| `admin.rs` | Mandanten-Einstellungen, Audit-Ansicht, Benutzerverwaltung |
| `dashboard.rs` | CSI-Kennzahlen (SLA-Erfüllung, MTTR, Verteilungen) |
| `forge.rs` | Forge-Contents-Client (get/update, Sha-Locking) |
Frontend: Askama-Templates (`templates/`), ein statisches CSS + ein JS
(`static/`), kein Framework, kein Inline-JS (CSP).
## 3. Request-Fluss
1. `ctx_middleware` (web.rs): Session-Cookie → SHA-256 → DB-Lookup (`sessions`
JOIN `users`/`tenants`) → `AuthUser` in Request-Extensions. Client-IP nur
aus X-Forwarded-For, wenn `ITSM_TRUSTED_PROXY_COUNT > 0`.
2. CSRF: jede zustandsändernde Methode braucht Token aus Session (Formular-
Feld `_csrf` oder Header `X-CSRF-Token`); Ausnahmen: `/login`, `/setup/new`.
3. Handler: RBAC-Check (`need_auth`/`need_operative`/`need_admin`/
`need_change_approver`) → Fachlogik → Audit-Log-Eintrag.
4. Antwort: Security-Header (CSP, nosniff, X-Frame-Options DENY, Referrer-
Policy, HSTS bei `ITSM_HTTPS=1`).
## 4. Datenmodell (PostgreSQL, schema.sql)
Alle fachlichen Tabellen tragen `tenant_id`; jede Query filtert darauf.
- `tenants` — Mandanten inkl. SLA-Defaults + DSGVO-Retention-Fristen
- `users` — global eindeutige E-Mail (Login löst Mandant auf), `role`
(admin/change_manager/agent/user), `auth_source` ('local', 'ldap' reserviert)
- `tickets` — inkl. ITIL-Feldern: `impact`/`urgency` (Priorität via Matrix),
`change_typ`/`approval_status`/`approved_by/-_at` (Change Enablement),
`problem_id`/`known_error` (Problem Mgmt), `first_response_at`/`resolved_at`/
`closed_at` (SLA-Zeitstempel)
- `ticket_timeline` — Worklog (Status, Kommentare, Repo-Edits: dokumentationspflichtig)
- `ticket_ci_links` — Ticket↔CI (SACM)
- `services`, `kb_articles` (mit `status` Entwurf/Freigegeben),
`configuration_items` + `ci_relationships`
- `audit_log` — alle sicherheitsrelevanten Aktionen (ISO 27001 A.12.4 / DSGVO Art. 30)
- `login_attempts` — Rate-Limit-Basis (7 Tage aufbewahrt)
- `sessions` — serverseitige Sessions, nur SHA-256-Hash des Cookie-Tokens
Migrationen: idempotenter Block am Ende von `schema.sql`, wird bei jedem
App-Start per `include_str!` ausgeführt (ADR-005).
## 5. RBAC-Matrix
| Aktion | admin | change_manager | agent | user |
|---|---|---|---|---|
| Eigene Tickets (Incident/Service Request) anlegen/sehen | ✓ | ✓ | ✓ | ✓ |
| Alle Tickets sehen/bearbeiten, Status wechseln | ✓ | ✓ | ✓ | |
| Probleme/Änderungen/Releases/Kanban/CMDB | ✓ | ✓ | ✓ | |
| KB lesen | alle | alle | alle | nur Freigegeben |
| KB anlegen/bearbeiten | ✓ | ✓ | ✓ | |
| KB freigeben, Change-Freigabe (CAB) | ✓ | ✓ | | |
| Repo-Edit aus Ticket (nur umsetzbarer Change) | ✓ | ✓ | | |
| Services anlegen, Administration, Audit-Log | ✓ | | | |
## 6. ITIL-Abbildung (v3-Prozesse / v4-Practices)
- Statusmodell: Offen → In Bearbeitung → Warten → Gelöst → Geschlossen;
Reopen Gelöst→Offen; Geschlossen final. Übergänge serverseitig erzwungen.
- Priorität = Matrix(Impact × Urgency), 3×3 nach v3 SO 4.2.5.4.
- Change Enablement: Standard (vorautorisiert) / Normal (CAB) / Emergency
(sofort, nachträgliche ECAB-Freigabe). Umsetzung + Repo-Edit erst wenn
`itil::change_may_be_implemented`.
- Problem Management: Incident↔Problem-Verknüpfung, Known-Error-Flag.
- SLA: Fristen aus Mandanten-Defaults, Zeitstempel bei Statuswechseln,
Überfälligkeits- und Restzeit-Berechnung serverseitig.
- Continual Improvement: /dashboard (SLA-Erfüllung 30 T, MTTR, Verteilungen).
## 7. Sicherheitsarchitektur
Siehe ADR-002/003/007/008 und README-Abschnitt "Sicherheit". Kurzfassung:
serverseitige widerrufbare Sessions, CSRF überall, DB-gestütztes Login-Rate-
Limit (E-Mail+IP), Argon2id mit transparenter Migration von Werkzeug-pbkdf2
UND -scrypt (ADR-003!), SSRF-Guard, CSP ohne Inline-JS, Audit-Log.

63
docs/BETRIEB.md Executable file
View File

@ -0,0 +1,63 @@
# ITSM — Betriebshandbuch
Stand: 2026-07-15. Produktivinstanz: SRV1361746 (`/docker/itsm/`), Port 8090.
## 1. ENV-Variablen
| Variable | Pflicht | Bedeutung |
|---|---|---|
| `DATABASE_URL` | ja | `postgresql://itsm:...@postgres:5432/itsm` |
| `ITSM_BIND` | | Bind-Adresse (Default `0.0.0.0:8090`) |
| `ITSM_HTTPS` | | `1` = Secure-Cookies + HSTS (hinter TLS-Terminierung setzen) |
| `ITSM_TRUSTED_PROXY_COUNT` | | Anzahl vertrauenswürdiger Reverse-Proxies; nur dann zählt X-Forwarded-For |
| `ITSM_SESSION_HOURS` | | Session-Lebensdauer (Default 8) |
| `ITSM_LOGIN_MAX_ATTEMPTS` / `ITSM_LOGIN_WINDOW_MINUTES` | | Rate-Limit (Default 5 / 15) |
| `ITSM_PASSWORD_MIN_LENGTH` | | Default 12 |
| `AES_DASHBOARD_URL` | | AES-Service im Katalog |
| `FORGE_BASE_URL` / `FORGE_SERVICE_TOKEN` | | Repo-Edit aus Change-Tickets; ohne = Feature deaktiviert |
| `RUST_LOG` | | z. B. `info` |
Hinweis: `ITSM_SECRET_KEY` (Flask-Ära) wird nicht mehr genutzt und kann aus
der Server-`.env` entfernt werden.
## 2. Deployment / Update (SRV1361746)
cd /docker/itsm
rm -rf src && git clone --depth 1 https://git1.mrmoe.de/mscadm/ITSM.git src
docker compose build itsm # Multi-Stage-Build, ~3 min
docker compose up -d # Postgres läuft weiter, nur App wird getauscht
curl -s localhost:8090/health # {"db":"ok","status":"ok"}
Es wird KEIN Code mehr beim Container-Start gezogen (früherer bootstrap.sh-
Mechanismus ist abgeschafft) — ein Neustart startet exakt das gebaute Image.
Schema-Migrationen laufen idempotent beim App-Start.
Rollback: `docker-compose.yml.flask-backup` liegt als letzter Flask-Stand in
`/docker/itsm/`; für das Rust-Image genügt `git clone` eines älteren Tags/
Commits nach `src/` + erneutes `build`.
## 3. Backup & Retention
- Täglicher pg_dump: `deploy/backup.sh` via Cron `0 3 * * *` nach
`/docker/itsm/backups/` (14 Tage Rotation). Live-Daten: Bind-Mount
`/docker/itsm/pgdata` (kein anonymes Docker-Volume).
- DSGVO-Retention: Hintergrundtask löscht gelöste/geschlossene Tickets und
Audit-Einträge nach Mandanten-Fristen; Sessions/Login-Versuche werden
mitbereinigt. Manuell: Admin → Einstellungen → "Jetzt manuell bereinigen".
## 4. Troubleshooting
| Symptom | Ursache / Abhilfe |
|---|---|
| Login schlägt für Bestandskonto fehl | Hash-Prefix prüfen: `docker exec itsm_postgres psql -U itsm -d itsm -c "SELECT email,left(password_hash,16) FROM users;"``pbkdf2:`/`scrypt:` = Werkzeug-Altbestand (wird unterstützt, ab Commit f732392 auch scrypt), `$argon2id$` = migriert. |
| "Zu viele Fehlversuche" (429) | Rate-Limit-Sperre; wartet 15 min oder `DELETE FROM login_attempts;` |
| 500er | `docker logs itsm_app --tail 50` (tracing-Log) |
| Health rot | DB-Verbindung prüfen (`docker ps`, `pg_isready`) |
| Repo-Edit-Button fehlt | FORGE_BASE_URL nicht gesetzt, Rolle < change_manager, oder Ticket ist kein freigegebener Change (gewollt, ADR-004) |
| Alle Sessions ungültig nach Deploy | erwartungsgemäß NICHT der Fall (Sessions liegen in der DB); falls doch: sessions-Tabelle prüfen |
## 5. Monitoring-Punkte
- `GET /health` (Docker-HEALTHCHECK eingebaut, curl-basiert)
- Audit-Log (Admin-UI) für login_failed/login_rate_limited-Häufungen
- Backup-Log `/docker/itsm/backup.log`

View File

@ -0,0 +1,23 @@
# ADR-001: Rust/axum statt Python/Flask (In-Place-Rewrite)
**Datum:** 2026-07-15 · **Status:** Akzeptiert · **Entscheider:** Moe (Vorgabe), umgesetzt via Claude
## Kontext
Die Flask-Version (single-file, ungepinnte Dependencies) hatte 8 Sicherheits-
Review-Befunde; Nutzer-Vorgabe: alle Schwachstellen beseitigen, technisch
aktuellster Stand, ITIL v3/v4, Umstellung auf Rust. Es existierte ein
Parallelbau-Plan (AES projects/itsm-rust) mit späterem Cutover.
## Entscheidung
Kompletter In-Place-Rewrite in Rust mit axum 0.7 + tokio-postgres/deadpool +
askama — gleicher Stack wie forge-web (einheitliche Technologierichtung).
Der Parallelbau-Plan wurde auf explizite Nutzer-Anweisung übersprungen.
## Konsequenzen
+ Ein Binary, kompilierte Templates, keine Laufzeit-Dependency-Drift mehr.
+ Stack-Konsistenz mit Forge (Wartbarkeit, Wissenstransfer).
Kein paralleles Rollback-System; kompensiert durch: identisches Schema
(nur additiv migriert), Werkzeug-Hash-Kompatibilität (ADR-003), E2E-Tests
vor Push, Compose-Backup als Rollback-Punkt.
Lehre aus Produktion (Forensik phase-010 B4): Kompatibilitätsannahmen
gegen ECHTE Produktionsdaten testen, nicht nur gegen eigene Vektoren.

View File

@ -0,0 +1,20 @@
# ADR-002: Serverseitige Sessions in PostgreSQL
**Datum:** 2026-07-15 · **Status:** Akzeptiert
## Kontext
Flask nutzte Client-Side-Sessions (signierte Cookies) mit optionalem Secret;
ohne gesetztes Secret wurde pro Prozessstart ein Zufallswert erzeugt
(Sessions bei Neustart weg, mehrere Worker inkompatibel). Sessions waren
nicht widerrufbar.
## Entscheidung
Sessions liegen in der DB (`sessions`-Tabelle). Cookie enthält nur ein
256-Bit-Zufallstoken; die DB speichert dessen SHA-256. CSRF-Token wird pro
Session serverseitig gehalten.
## Konsequenzen
+ Widerrufbar (Logout, Kontosperrung löscht Sessions), neustart- und
multi-instanz-fest, kein Secret-Management (ITSM_SECRET_KEY entfällt).
+ DB-Leak kompromittiert keine laufenden Sessions (nur Hashes).
Ein DB-Roundtrip pro Request (Middleware); bei aktueller Last irrelevant.

View File

@ -0,0 +1,23 @@
# ADR-003: Argon2id + Werkzeug-Kompatibilitätsschicht (pbkdf2 UND scrypt)
**Datum:** 2026-07-15 · **Status:** Akzeptiert (ergänzt um scrypt nach Prod-Befund B4)
## Kontext
Bestandskonten wurden von Werkzeug (Flask) gehasht. Zwangs-Passwort-Reset
für den Produktiv-Mandanten war inakzeptabel. Werkzeug erzeugt je nach
Version pbkdf2:sha256- ODER scrypt-Hashes (>= 3.0 Default: scrypt) — die
Flask-Ära installierte ungepinnt, in Produktion lagen scrypt-Hashes.
## Entscheidung
Neue Hashes: Argon2id (RustCrypto-Defaults). verify_password() erkennt
Werkzeug-Formate (`pbkdf2:sha256:*` und `scrypt:n:r:p$salt$hex`), verifiziert
in Konstantzeit und rehasht beim ersten erfolgreichen Login transparent auf
Argon2id (schleichende Migration, kein Reset).
## Konsequenzen
+ Bestandslogins funktionieren unverändert; Bestand härtet sich selbst nach.
Werkzeug-Kompatibilitätscode bleibt, bis alle Konten migriert sind
(prüfbar: `SELECT count(*) FROM users WHERE password_hash NOT LIKE '$argon2%'`).
Lehre: Der erste Wurf unterstützte nur pbkdf2 und fiel in Produktion auf
(Login-Ausfall + Rate-Limit-Sperre). Hash-Kompatibilität ist künftig gegen
reale Prod-Hash-Prefixe zu verifizieren (Forensik phase-010, Befund B4).

View File

@ -0,0 +1,23 @@
# ADR-004: ITIL-Rollenmodell + Change-Gate für Repo-Edits
**Datum:** 2026-07-15 · **Status:** Akzeptiert · **Vorgabe:** "Rollen gemäß ITIL"
## Kontext
Vorher nur admin/agent; Repo-Edit aus Tickets stand JEDEM eingeloggten
Nutzer offen (mit gemeinsamem Forge-Admin-Token) — Review-Befund.
## Entscheidung
Vier Rollen: admin (Service Owner), change_manager (Change Enablement/CAB),
agent (Service Desk), user (Requester/Self-Service: eigene Tickets, nur
freigegebene KB). Repo-Edits gelten als Change-Implementierung: erlaubt nur
für admin/change_manager UND nur aus einem umsetzbaren Change-Ticket
(Standard = vorautorisiert; Normal = nach CAB-Genehmigung; Emergency =
sofort mit nachträglicher ECAB-Freigabe; Abgelehnt = nie). Jede Repo-
Änderung bleibt worklog-pflichtig (phase-008-Garantie unverändert).
## Konsequenzen
+ Least-Privilege; auditierbarer Change-Pfad für Produktions-Repos.
Agents müssen für Repo-Änderungen einen Change anlegen und freigeben
lassen — gewollte Prozesshürde.
Feingranulare Forge-Repo-Rechte (statt Admin-Token) bleiben offen, bis
Forge Repo-Scopes kennt (Backlog).

View File

@ -0,0 +1,19 @@
# ADR-005: Idempotente Schema-Migrationen statt Migrationstool
**Datum:** 2026-07-15 · **Status:** Akzeptiert
## Kontext
Bestehende Prod-DB (Flask-Ära) musste ohne Migrationsschritt weiterverwendet
werden; das Projekt hat genau eine schema.sql-Historie und wenige Releases.
## Entscheidung
`schema.sql` bleibt die einzige Quelle: CREATE TABLE IF NOT EXISTS +
ADD COLUMN IF NOT EXISTS + einmalige, selbstneutralisierende UPDATEs.
Wird beim App-Start per `include_str!` komplett ausgeführt (batch_execute).
## Konsequenzen
+ Kein Migrationstool, keine Versionstabelle, Deploy = Start.
+ Kompilierzeit-Einbettung: Binary und Schema sind untrennbar konsistent.
Destruktive Änderungen (DROP/RENAME) brauchen künftig explizite Sorgfalt
oder den Wechsel auf ein echtes Migrationstool — akzeptiert für die
aktuelle Projektgröße. Additiv-only ist Konvention.

View File

@ -0,0 +1,20 @@
# ADR-006: Multi-Stage-Docker-Build statt Clone-on-Start
**Datum:** 2026-07-15 · **Status:** Akzeptiert
## Kontext
Der frühere bootstrap.sh klonte bei JEDEM Container-Start main und
installierte ungepinnte pip-Pakete: unreproduzierbar, ungetesteter Stand
konnte durch bloßen Neustart live gehen; forge_client.py wurde dabei sogar
vergessen (Feature in Prod defekt).
## Entscheidung
Multi-Stage-Dockerfile (rust:1-slim-bookworm → debian:bookworm-slim,
USER nobody, HEALTHCHECK). deploy/docker-compose.yml baut per `build:` aus
dem geklonten Quellstand; Neustart startet exakt das gebaute Image.
## Konsequenzen
+ Reproduzierbare, testbare Deployments; Rollback = altes Image/älterer Commit.
Update erfordert bewussten Build (~3 min auf SRV1361746) — gewollt.
postgres:16-alpine wird beibehalten (pgdata-Kompatibilität); Major-Upgrade
wäre ein eigener Change mit pg_upgrade/dump+restore.

View File

@ -0,0 +1,20 @@
# ADR-007: CSP ohne Inline-JavaScript
**Datum:** 2026-07-15 · **Status:** Akzeptiert
## Kontext
Die Flask-Version renderte onclick-Handler und <script>-Blöcke inline;
eine wirksame Content-Security-Policy war so unmöglich.
## Entscheidung
`script-src 'self'`: alles JavaScript liegt in static/app.js (Event-
Delegation, data-Attribute); zustandsändernde fetches senden X-CSRF-Token.
`style-src 'self' 'unsafe-inline'`: Inline-STYLE-ATTRIBUTE bleiben erlaubt
(dynamische Balkenbreiten etc.) — bewusste, dokumentierte Ausnahme; Inline-
<script> bleibt verboten. Zusätzlich: X-Frame-Options DENY, nosniff,
Referrer-Policy same-origin, HSTS bei ITSM_HTTPS=1.
## Konsequenzen
+ XSS-Einschlag drastisch reduziert (kein Skript außerhalb /static ausführbar).
style-src-Ausnahme ist ein Rest-Risiko (CSS-Injection); akzeptiert, da
alle Ausgaben escaped werden (Askama-Autoescape + esc() im JS).

View File

@ -0,0 +1,21 @@
# ADR-008: SSRF-Policy für Service-Endpoints
**Datum:** 2026-07-15 · **Status:** Akzeptiert
## Kontext
Der Service-Katalog prüft hinterlegte Endpoint-URLs serverseitig auf
Erreichbarkeit. Interne Dienste (AES-Dashboard, Forge) sind der Hauptzweck —
ein pauschales Verbot privater Adressen wäre selbstzerstörerisch; völlige
Freiheit wäre eine SSRF-Fläche.
## Entscheidung
Dreistufig: (1) Nur http/https. (2) Link-Local-/Cloud-Metadaten-Bereiche
(169.254.0.0/16, fe80::/10) sind immer blockiert, auch nach DNS-Auflösung.
(3) Private Adressen bleiben erlaubt, ABER Anlegen/Ändern von Services ist
admin-only. Checks laufen parallel mit 3 s Timeout und 30 s Cache (kein
Blockieren des Katalogs, kein Amplification-Hebel).
## Konsequenzen
+ Interner Hauptzweck bleibt nutzbar; Metadaten-Endpoints unerreichbar.
Ein böswilliger ADMIN könnte interne Adressen sondieren — akzeptiertes
Restrisiko (Admin ist ohnehin höchstprivilegiert, Aktionen auditiert).

16
docs/adr/README.md Executable file
View File

@ -0,0 +1,16 @@
# Architecture Decision Records (ADRs)
Format: Kontext → Entscheidung → Konsequenzen. Status: Akzeptiert, sofern
nicht anders vermerkt. Nummerierung chronologisch, Änderungen nur durch
neue, ersetzende ADRs (nie stillschweigend editieren).
| Nr | Titel | Datum |
|---|---|---|
| 001 | Rust/axum statt Python/Flask (In-Place-Rewrite) | 2026-07-15 |
| 002 | Serverseitige Sessions in PostgreSQL | 2026-07-15 |
| 003 | Argon2id + Werkzeug-Kompatibilitätsschicht (pbkdf2 UND scrypt) | 2026-07-15 |
| 004 | ITIL-Rollenmodell + Change-Gate für Repo-Edits | 2026-07-15 |
| 005 | Idempotente Schema-Migrationen statt Migrationstool | 2026-07-15 |
| 006 | Multi-Stage-Docker-Build statt Clone-on-Start | 2026-07-15 |
| 007 | CSP ohne Inline-JavaScript | 2026-07-15 |
| 008 | SSRF-Policy für Service-Endpoints | 2026-07-15 |