Zwei unabhaengige Aufgaben: user-files als benanntes Volume in beide Repository-Compose-Dateien plus Betriebshandbuch Kapitel 6/7 (WINDOWS #17), und die Technik-Tabelle in CLAUDE.md auf den installierten Stand bringen. Beide Abnahmetore vor der Arbeit als rot gemessen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
32 KiB
quick_id, slug, date, status, relates_to, windows_ref, severity, phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, estimate, must_haves
| quick_id | slug | date | status | relates_to | windows_ref | severity | phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | estimate | must_haves | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 260909-cx0 | dateisicherung-nachruesten-und-versionsa | 2026-09-09 | planned | 06-desktop-client-ci-cd, 07-dkv-fleet-module | 17 | medium | quick-260909-cx0 | 01 | execute | 1 |
|
true |
|
|
|
Punkt A (WINDOWS #17) — hochgeladene Dateien ueberleben kein Neuerstellen der Container.
Der Code legt Profilbilder und DKV-Exporte unter user-files/ ab, aber keine Compose-Datei
im Repository mountet dieses Verzeichnis. Es existiert damit nur in der beschreibbaren
Container-Schicht und ist bei jedem up -d --force-recreate weg. Bisher ist kein Schaden
entstanden — es hat noch kein Nutzer ein Profilbild hinterlegt —, aber der erste Upload
faellt in dieselbe Grube.
Punkt B — CLAUDE.md nennt Fassungen, die nicht installiert sind.
Die Technik-Tabelle ist eine woertliche Kopie der Stack-Empfehlung von 2026-06/07 und
beschreibt einen Wunschzustand: Next.js 16.2.x statt der installierten 15.5.19, Prisma 7.8.x
statt 6.19.3, dazu Keycloak, Redis, TanStack Query, shadcn/ui, Playwright, Husky und
lint-staged, von denen nichts im Projekt existiert. Das neue Entwicklungshandbuch
(docs/anleitung-entwicklung.md) dokumentiert bewusst den echten Stand — beide Dokumente
widersprechen sich damit schriftlich.
Purpose: Nutzerdaten ueberstehen einen Redeploy, und wer neu dazustoesst, liest in CLAUDE.md, was wirklich installiert ist, statt was einmal empfohlen wurde. Output: Zwei getrennte, jeweils fuer sich lauffaehige Commits — A (Datenhaltung + Handbuch), B (Dokumentationskorrektur).
Gemessener Ausgangszustand (am Arbeitsbaum geprueft, 2026-09-09)
Punkt A
| Beleg | Fundstelle |
|---|---|
Profilbilder werden nach user-files/avatars/ geschrieben |
apps/api/src/user/user.controller.ts:40 (path.resolve(__dirname, '..','..','..','..','user-files','avatars')), Ablage :249-266 |
DKV-Exporte werden nach user-files/ geschrieben |
apps/api/src/dkv/dkv-export.service.ts:59 (gleiche Aufloesung), Schreiben :135 |
Im Image ist der Zielpfad /app/user-files, angelegt und uebereignet an uid 1001 |
apps/api/Dockerfile:21 (WORKDIR /app), :23-26 (mkdir -p /app/user-files + chown nestjs:nestjs), :36 (USER nestjs) |
docker-compose.yml kennt nur ein Volume, pgdata; der Dienst api hat gar keinen volumes-Block |
docker-compose.yml:20-65 (api) und :92-93 (Top-Level-Volumes) |
docker-compose.prod.yml ebenso |
docker-compose.prod.yml:22-61 (api) und :89-90 |
docker-compose.dev.yml mountet nur Quellcode und Prisma-Schema |
docker-compose.dev.yml:7-10 |
Gerenderte Konfiguration traegt fuer api keinen Mount auf /app/user-files — in allen drei Kombinationen |
selbst gemessen mit docker compose config --format json fuer Basis, Prod und Basis+Dev: dreimal FEHLT |
| Das Betriebshandbuch beschreibt die Luecke bereits und raet zu manuellem Herauskopieren | docs/anleitung-betrieb.md:278-290 (Kapitel 6) und :316 (Fehlertabelle Kapitel 7) |
Punkt B
Verglichen wurden die Tabellenwerte in CLAUDE.md:26-97 gegen package.json (Wurzel und je App)
und die in pnpm-lock.yaml aufgeloesten Fassungen (importers:-Abschnitt), dazu die
Compose- und Dockerfiles.
| CLAUDE.md sagt | Tatsaechlich installiert (2026-09-09) |
|---|---|
Next.js 16.2.x (:39) |
15.5.19 (Vorgabe ^15.3.0) |
Prisma 7.8.x (:56) |
6.19.3 — prisma und @prisma/client, Vorgabe ^6.0.0; auch apps/api/Dockerfile:33 nennt 6.19.3 |
Keycloak 26.6.x als Identitaetsanbieter (:64), nest-keycloak-connect (:65) |
nicht vorhanden — kein Keycloak-Dienst in irgendeiner Compose-Datei, kein Paket im Lockfile. Angemeldet wird ueber @nestjs/jwt 11.0.2, passport 0.7.0, argon2 0.44.0, Verzeichnisanbindung ueber ldapts 8.1.8 |
Redis 7.x als Cache/Sitzungsspeicher (:58) |
nicht vorhanden — kein Dienst, kein Paket |
TanStack Query 5.101.x (:48) |
nicht installiert |
shadcn/ui CLI v4 (:43) |
nicht benutzt — keine components.json, kein components/ui-Verzeichnis |
Playwright 1.x als E2E-Test (:88) |
keine Projektabhaengigkeit — Browserpruefungen laufen ueber das Playwright-MCP-Werkzeug, im Repository existiert keine E2E-Suite und keine playwright.config.* |
Husky 9.x (:96), lint-staged 15.x (:97) |
nicht installiert, kein .husky-Verzeichnis |
Vitest 3.x (:87) |
gemischt: apps/api 3.2.6, apps/web 4.1.9 |
Docker 27.x / Compose 2.x (:78-79) |
Wirtseigenschaft, vom Repository nicht gesetzt; auf dieser Maschine gemessen: Docker 29.8.0, Compose v5.5.1 |
Tauri 2.x (:72) |
2.11.1 / CLI 2.11.3 — stimmt; apps/desktop ist laut docs/anleitung-entwicklung.md:38-40 bisher nur Geruest |
| pnpm 9.x, Turborepo 2.9.x, React 19.x, TypeScript 5.5+, Tailwind 4.3.x, next-themes 0.4.x, next-intl 4.13.x, react-grid-layout 2.2.x, Zustand 5.0.x, NestJS 11.x, Express 5.x, PostgreSQL 16.x, Biome 2.x, Testing Library | stimmen. Genau: pnpm 9.15.0, Turborepo 2.9.18, React/React-DOM 19.2.7, TypeScript 5.9.3, Tailwind 4.3.1, next-themes 0.4.6, next-intl 4.13.0, react-grid-layout 2.2.3, Zustand 5.0.14, NestJS 11.1.27, Express 5.2.1 (mittelbar ueber @nestjs/platform-express), postgres:16-alpine, Biome 2.5.0, @testing-library/react 16.3.2 |
| (fehlt in der Tabelle) | Node 24 — beide produktiven Dockerfiles ziehen node:24-alpine |
Zur Verteilung auf den Server (bitte woertlich weitergeben, NICHT ausfuehren)
/opt/tessera/docker-compose.yml ist keine Arbeitskopie dieses Repositorys. Die Datei
wurde dort von Hand bearbeitet und weicht ab (Sicherung docker-compose.yml.bak.20260811).
Der Deploy holt ausschliesslich Images. Eine Aenderung an den Compose-Dateien im Repository
erreicht die laufende Installation deshalb nie.
Damit die Reparatur auf alpha wirkt, muss der Nutzer dieselben zwei Zeilen selbst in
/opt/tessera/docker-compose.yml eintragen und die Container einmal neu erstellen. Dieser
Plan sieht dafuer keine Handlung vor: kein SSH, kein docker-Aufruf gegen irgendeinen
Server, keine Datei ausserhalb dieses Arbeitsbaums. Das Betriebshandbuch bekommt die
Anleitung dafuer schriftlich, damit sie nicht in einer Sitzung verloren geht.
Solange das nicht geschehen ist, bleibt der Ledger-Eintrag WINDOWS #17 offen — die
Luecke besteht auf der laufenden Installation weiter. Der Executor schliesst ihn nicht;
das Schliessen ist die Entscheidung des Nutzers, nachdem er die Serverdatei ergaenzt hat
(gsd-tools windows fixed 17).
Zur Wahl der Speicherart (Begruendung, wie gefordert)
Gewaehlt: benanntes Volume user-files, gemountet auf /app/user-files.
Ausschlaggebend ist die Eigentuemerschaft. Das Image legt /app/user-files an und uebereignet
es uid 1001 (apps/api/Dockerfile:23-26), der Prozess laeuft als dieser Nutzer (:36). Ein
leeres benanntes Volume uebernimmt beim ersten Mounten Inhalt und Eigentuemerschaft des
Image-Verzeichnisses — Schreiben funktioniert sofort. Ein Host-Verzeichnis, das Docker beim
Start neu anlegt, gehoert dagegen root; der API-Prozess koennte nicht hineinschreiben, und aus
einem Datenverlust wuerde ein kaputter Upload. Ein Bind-Mount waere also nur mit einer
zusaetzlichen Handlung des Betreibers (Verzeichnis anlegen und auf 1001 uebereignen) korrekt —
genau die Art stiller Voraussetzung, die auf dem Server erfahrungsgemaess untergeht.
Zur Sicherung passt das: das Betriebshandbuch nennt in Kapitel 6 bereits
docker compose cp api:/app/user-files ./user-files-backup, und dieser Befehl funktioniert
mit einem benannten Volume unveraendert weiter — nur ist er kuenftig eine Sicherung und keine
Rettung mehr. Zusaetzlich passt die Wahl zum bereits vorhandenen pgdata, das dieselbe Form hat.
Das Handbuch muss dennoch angefasst werden, weil Kapitel 6 heute das Gegenteil behauptet.
<execution_context>
@/.claude/gsd-core/workflows/execute-plan.md
@/.claude/gsd-core/templates/summary.md
</execution_context>
Dateien, die beim Umsetzen gelesen werden muessen: @docker-compose.yml @docker-compose.prod.yml @docs/anleitung-betrieb.md
Belege, die nicht veraendert werden (nur zum Nachschlagen):
apps/api/Dockerfile— Zeilen 21-26 und 36 begruenden die Wahl des benannten Volumes.apps/api/src/user/user.controller.ts:40undapps/api/src/dkv/dkv-export.service.ts:59— beide Schreibpfade, beide landen im Container auf/app/user-files.docs/anleitung-entwicklung.md— nennt bereits den echten Stand (pnpm 9.15.0,node:24-alpine, Biome, Vitest in beiden Apps). Aufgabe B muss dazu passen, nicht davon abweichen.pnpm-lock.yaml, Abschnittimporters:— die verbindliche Quelle fuer die aufgeloesten Fassungen. Nicht diepackage.json-Vorgaben (^15.3.0) als Fassung ausgeben.
Verbindliche Konventionen aus dem Bestand:
- Die Handbuecher unter
docs/sind deutsch mit echten Umlauten (für,über). Neue Saetze dort in derselben Schreibweise. Der Umlaut-Test inapps/web/src/messagesbetrifft nur die Oberflaechentexte, nicht diese Dokumente. - Der Technik-Block in CLAUDE.md ist englisch. Er bleibt englisch — dies ist eine Zahlenkorrektur, keine Uebersetzung.
Die Docker-CLI ist lokal aufrufbar (docker compose version antwortet). Die Pruefung rendert ausschliesslich Konfiguration und startet, baut und stoppt nichts.
Die Speicherart laesst sich spaeter aendern, aber nicht folgenlos: ein Wechsel auf ein Host-Verzeichnis erfordert einmaliges Umkopieren des Volume-Inhalts und eine Uebereignung an uid 1001.
Beide Compose-Dateien im Repository bekommen denselben Zusatz. Nichts anderes aendern — keine Umgebungsvariablen, keine Ports, keine Healthchecks.In `docker-compose.yml`: beim Dienst `api` einen Block `volumes:` einfuegen (Einrueckung wie
bei `networks:` desselben Dienstes) mit dem einzigen Eintrag `- user-files:/app/user-files`.
Sinnvolle Stelle ist direkt vor `healthcheck:` (heute Zeile 60). Anschliessend im
Top-Level-Block `volumes:` (heute Zeile 92-93) neben `pgdata:` einen zweiten Eintrag
`user-files:` ergaenzen — ohne Wert, das ist ein benanntes Volume mit Vorgaben.
In `docker-compose.prod.yml`: identisch, Dienst `api` (Block vor `healthcheck:`, heute
Zeile 56) und Top-Level-Block `volumes:` (heute Zeile 89-90).
`docker-compose.dev.yml` bleibt unveraendert. Grund, gemessen: Compose fuehrt die
Mount-Listen ueber das Ziel zusammen, die Kombination Basis + Dev traegt den neuen Mount
also automatisch mit — nachgewiesen an der gerenderten Konfiguration. Ein zweiter Eintrag
dort waere Doppelpflege.
Danach `docs/anleitung-betrieb.md`, Kapitel 6, Abschnitt "Was sonst noch an Zustand
existiert" (heute Zeile 269-290). Drei Dinge:
(1) Der erste Aufzaehlungspunkt (`PostgreSQL-Daten`, Zeile 271-277) behauptet, `pgdata` sei
der einzige dauerhafte Datenspeicher. Das stimmt nach dieser Aenderung nicht mehr — Satz so
umformulieren, dass es zwei benannte Volumes gibt. Den vorhandenen Schreibfehler
"daürhafte" dabei mitkorrigieren.
(2) Der Aufzaehlungspunkt `Hochgeladene Dateien` (Zeile 278-290) wird ersetzt. Er muss
kuenftig sagen: die Dateien liegen im benannten Volume `user-files`, gemountet auf
`/app/user-files` im Dienst `api`, eingetragen in `docker-compose.yml` und
`docker-compose.prod.yml`; sie ueberstehen ein `--force-recreate`; gesichert werden sie
weiterhin mit `docker compose cp api:/app/user-files ./user-files-backup`, alternativ ueber
eine Sicherung des Volumes; das Volume traegt im `docker volume ls` den Projektnamen als
Praefix (`<projekt>_user-files`). Die Verweise auf `apps/api/src/user/user.controller.ts`
und `apps/api/src/dkv/dkv-export.service.ts` als Schreibstellen bleiben erhalten. Zeigen Sie
dabei einen kurzen YAML-Ausschnitt mit genau den zwei neuen Zeilen — die Dienst-Zeile in der
Form `user-files:/app/user-files` und den Top-Level-Eintrag —, damit ein Betreiber sie
kopieren kann. Diese Zeichenkette ist Teil der Abnahmepruefung.
(3) Ein deutlich abgesetzter Hinweis im selben Abschnitt: `/opt/tessera/docker-compose.yml`
auf dem Server ist keine Arbeitskopie des Repositorys, wurde von Hand bearbeitet und wird
von einem Deploy nicht angefasst. Wer die Reparatur dort haben will, traegt dieselben zwei
Zeilen selbst ein (vorher sichern) und erstellt die Container einmal neu. Bis dahin gilt
fuer die laufende Installation weiterhin der alte, verlustbehaftete Zustand; pruefbar mit
`docker inspect tessera-api-1` und einem Blick auf `Mounts`.
Schliesslich Kapitel 7, Fehlertabelle, Zeile 316 ("Avatare/DKV-Exporte nach einem Deploy
verschwunden"): die Ursachenspalte trifft nach dieser Aenderung nur noch auf eine
Installation zu, deren Compose-Datei den Mount nicht hat. Zeile entsprechend umschreiben
und als Abhilfe den Eintrag der zwei Zeilen samt Verweis auf Kapitel 6 nennen, nicht mehr
das vorherige "langfristig ergaenzen".
Ausdruecklich nicht Teil dieser Aufgabe: irgendein Aufruf gegen alpha oder einen anderen
Server, `docker compose up`, `pull`, `restart` oder das Anlegen von Verzeichnissen auf
einem Host.
node -e "const {execFileSync}=require('child_process');const fs=require('fs');const env={...process.env,TESSERA_ENCRYPTION_KEY:'x',DATABASE_URL:'x',JWT_SECRET:'x',DB_PASSWORD:'x',TESSERA_ADMIN_EMAIL:'x',TESSERA_ADMIN_PASSWORD:'x'};const r=a=>JSON.parse(execFileSync('docker',['compose',...a,'config','--format','json'],{env}));const c=(l,cfg)=>{const m=(cfg.services.api.volumes||[]).filter(v=>v.target==='/app/user-files');console.log(l,m.length===1?'OK '+m[0].type+':'+m[0].source:'FEHLT');return m.length===1;};const doc=fs.readFileSync('docs/anleitung-betrieb.md','utf8').includes('user-files:/app/user-files');console.log('Betriebshandbuch nennt die Mount-Zeile',doc?'OK':'FEHLT');const res=[c('basis',r(['-f','docker-compose.yml'])),c('prod',r(['-f','docker-compose.prod.yml'])),c('basis+dev',r(['-f','docker-compose.yml','-f','docker-compose.dev.yml'])),doc];process.exit(res.every(Boolean)?0:1)"
Am 2026-09-09 gegen den unveraenderten Arbeitsbaum ausgefuehrt: alle vier Pruefungen melden FEHLT, Rueckgabewert 1. Gegen eine Kopie mit dem hier beschriebenen Zusatz melden die drei Compose-Pruefungen `OK volume:user-files`, Rueckgabewert 0. Das Tor ist also echt rot und wird durch genau diese Aenderung gruen.
Alle drei gerenderten Konfigurationen (Basis, Prod, Basis+Dev) tragen fuer den Dienst `api`
genau einen Mount vom Typ `volume` mit Quelle `user-files` auf `/app/user-files`; beide
Compose-Dateien fuehren `user-files` als benanntes Top-Level-Volume neben `pgdata`;
`docker-compose.dev.yml` ist unveraendert; Kapitel 6 des Betriebshandbuchs beschreibt den
Speicher als dauerhaft, nennt die Mount-Zeile woertlich, nennt den Sicherungsbefehl und
weist auf die abweichende Serverdatei hin; die Fehlerzeile in Kapitel 7 passt dazu; kein
Aufruf gegen einen Server wurde ausgefuehrt; WINDOWS #17 bleibt offen.
Task 2: Versionsangaben in CLAUDE.md auf den installierten Stand bringen
CLAUDE.md, .planning/research/STACK.md
Reine Dokumentationsaenderung, jederzeit zuruecknehmbar.
Nur Text. Keine Abhaengigkeit wird aktualisiert, kein `pnpm add`, kein `pnpm update`, `package.json` und `pnpm-lock.yaml` bleiben unangetastet.Zuerst die Zahlen selbst nachschlagen und nicht aus diesem Plan uebernehmen: `pnpm-lock.yaml`,
Abschnitt `importers:`, liefert je Arbeitsbereich Vorgabe und aufgeloeste Fassung. Verbindlich
ist die aufgeloeste Fassung. Fuer Dienste ausserhalb von npm gelten die Compose- und
Dockerfiles (`postgres:16-alpine`, `node:24-alpine`). Die Tabelle im `objective` dieses Plans
ist der am 2026-09-09 gemessene Stand und dient als Gegenprobe — weicht Ihr Befund ab,
zaehlt Ihr Befund, und die Abweichung gehoert in die Zusammenfassung.
Dann den Block zwischen `<!-- GSD:stack-start ... -->` und `<!-- GSD:stack-end -->`
(CLAUDE.md Zeile 22-169) ueberarbeiten. Der Block bleibt englisch.
(a) Direkt unter die Ueberschrift `## Technology Stack` einen kurzen Herkunftsvermerk setzen:
dass die Tabellen den installierten Stand zeigen, am 2026-09-09 gegen `package.json`,
`pnpm-lock.yaml` und die Compose-/Dockerfiles geprueft; dass die urspruengliche Empfehlung
von 2026-06/07 in `.planning/research/STACK.md` liegt; und dass eine Regeneration dieses
Blocks aus jener Datei die Zahlen wieder verfaelschen wuerde.
(b) In den Technik-Tabellen jede Versionsangabe auf die tatsaechlich aufgeloeste Fassung
setzen. Die Spaltenueberschrift so benennen, dass klar ist, dass dort der Ist-Stand steht.
Ergaenzen Sie eine Zeile fuer Node (`node:24-alpine`, aus beiden produktiven Dockerfiles),
weil das die verbindliche Laufzeit ist und bisher fehlt. Vitest bekommt beide Fassungen mit
Angabe der App, weil sie sich unterscheiden. Bei Docker und Docker Compose gehoert dazu,
dass es Wirtseigenschaften sind, die das Repository nicht festlegt — mit der auf der
Entwicklungsmaschine gemessenen Fassung und Datum. Bei den Authentifizierungs-Zeilen tritt
an die Stelle des nie eingebauten Identitaetsanbieters, was wirklich laeuft: `@nestjs/jwt`,
`passport` mit `@nestjs/passport`, `argon2` fuer Passwoerter und `ldapts` fuer die
Verzeichnisanbindung; `@nestjs/passport` traegt heute ausserdem eine falsche Zweckangabe
(angeblich Schluessel-Authentifizierung zwischen Modulen) — richtig ist der Einsatz fuer die
Anmelde- und JWT-Strategien.
(c) Alles, was empfohlen, aber nie uebernommen wurde, verschwindet aus den Ist-Tabellen und
erscheint stattdessen in einem neuen, deutlich benannten Abschnitt im selben Block, direkt
unter den Tabellen: die Empfehlung, was stattdessen im Einsatz ist, und woher die Empfehlung
stammt. Betroffen sind der Identitaetsanbieter samt zugehoerigem NestJS-Paket, der
Cache-Dienst, die Server-State-Bibliothek, die Komponentenbibliothek, der E2E-Testlaeufer und
die beiden Git-Hook-Werkzeuge; ebenso die beiden Faelle, in denen zwar dieselbe Technik, aber
ein aelterer Hauptstand installiert ist (Next.js und Prisma) — dort mit dem klaren Vermerk,
dass die neuere Fassung empfohlen, aber nicht uebernommen wurde, und ohne jede Aussage
darueber, ob eine Aktualisierung geplant sei.
Dieser Abschnitt MUSS eine Aufzaehlung sein, keine Tabelle. Die Abnahmepruefung verwirft
jede Zeile, die eine dieser Techniken als erste Tabellenzelle fuehrt — das ist genau die
Form, die "ist eingebaut" behauptet.
(d) Die drei Anschluss-Abschnitte im selben Block angleichen, damit sie der korrigierten
Tabelle nicht widersprechen:
- `## Alternatives Considered` (Zeile 99-117): einen Einleitungssatz voranstellen, dass die
Tabelle die Entscheidungslage von 2026-06 festhaelt und ihre Spalte "Recommended" keine
Aussage ueber den heutigen Stand ist. Die Tabelle selbst bleibt inhaltlich stehen.
- `## Version Pinning Strategy` (Zeile 147-152): die Beispielangaben auf die installierten
Haupt-Fassungen bringen; die Zeile zum Identitaetsanbieter-Image ist gegenstandslos und
gehoert in die Aufzaehlung aus (c) statt in eine Regel.
- `## Sources` (Zeile 154-167): als Quellen der damaligen Empfehlung kennzeichnen, nicht als
Belege des Ist-Stands. Die Links bleiben.
- `## Multi-Tenancy Strategy` (Zeile 119-125) bleibt: Prisma Client Extensions sind
tatsaechlich im Einsatz (`apps/api/src/prisma/prisma-tenant.extension.ts`). Diesen Beleg
dort ergaenzen.
(e) Zuletzt `.planning/research/STACK.md`: unter die Ueberschrift `# v1.0 Base Stack
(reference — unchanged)` (Zeile 97) eine einzelne, abgesetzte Hinweiszeile setzen, dass es
sich um die Empfehlung vom Juni/Juli 2026 handelt, dass sie teilweise nicht uebernommen
wurde und dass der installierte Stand in CLAUDE.md steht. Nichts loeschen, keine Zahl in
diesem Dokument aendern — es ist ein datiertes Rechercheergebnis und bleibt als solches
lesbar.
Zum Schluss `docs/anleitung-entwicklung.md` gegenlesen (nicht aendern) und in der
Zusammenfassung bestaetigen, dass keine Versionsangabe der beiden Dokumente einander mehr
widerspricht.
node -e "const t=require('fs').readFileSync('CLAUDE.md','utf8').split('\n');const has=re=>t.some(l=>re.test(l));const bad=t.filter(l=>/^\| (Keycloak|Redis|TanStack Query|Husky|lint-staged|Playwright|nest-keycloak-connect|shadcn\/ui) \|/.test(l));const res='Next-Zeile nennt 15.5.19',has(/^\;for(const e of res)console.log(e[1]?'OK ':'ROT ',e[0]);if(bad.length)console.log('Treffer:\n'+bad.map(l=>l.slice(0,50)).join('\n'));process.exit(res.every(e=>e[1])?0:1)"
git diff --exit-code -- package.json apps/api/package.json apps/web/package.json apps/desktop/package.json packages/shared/package.json packages/module-sdk/package.json pnpm-lock.yaml
Erste Pruefung am 2026-09-09 gegen den unveraenderten Arbeitsbaum ausgefuehrt: alle drei Punkte ROT, acht Trefferzeilen, Rueckgabewert 1; gegen eine korrigierte Kopie Rueckgabewert 0. Die zweite Pruefung ist heute gruen und bleibt es nur, solange keine Abhaengigkeit angefasst wird — sie ist die Absicherung gegen ein versehentliches Aktualisieren.
Jede Versionsangabe im Technik-Block von CLAUDE.md entspricht der in `pnpm-lock.yaml`
aufgeloesten Fassung beziehungsweise den Compose-/Dockerfiles; eine Node-Zeile ist ergaenzt;
Vitest ist mit beiden Fassungen je App gefuehrt; Docker/Compose sind als Wirtseigenschaft
mit Messdatum gekennzeichnet; die Authentifizierungs-Zeilen beschreiben die eingebaute
eigene Anmeldung statt eines Identitaetsanbieters; alle nie uebernommenen Empfehlungen
stehen sichtbar in einer Aufzaehlung statt in den Ist-Tabellen; Herkunftsvermerk gesetzt;
`Alternatives Considered`, `Version Pinning Strategy` und `Sources` widersprechen der
Tabelle nicht mehr; `.planning/research/STACK.md` traegt eine Hinweiszeile und ist sonst
unveraendert; `package.json` und `pnpm-lock.yaml` sind unveraendert.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| beschreibbare Container-Schicht -> dauerhafter Speicher | Von Nutzern hochgeladene Inhalte (Profilbilder, DKV-Exporte) verlassen die fluechtige Schicht und ueberdauern den Container |
| Host-Dateisystem <-> Container (verworfene Bind-Mount-Variante) | Ein Host-Verzeichnis waere ein zweiter Zugriffsweg auf Nutzerdaten, vorbei an der API |
| Dokument -> Leser/Agent | CLAUDE.md praegt Annahmen darueber, welche Schutzmechanismen angeblich vorhanden sind |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-cx0-01 | Information Disclosure | benanntes Volume user-files (docker-compose.yml, docker-compose.prod.yml) |
low | accept | Es entsteht keine neue Zugriffsflaeche: die Dateien werden weiterhin ausschliesslich ueber authentifizierte Endpunkte ausgeliefert (GET /users/me/avatar streamt aus dem in der Datenbank hinterlegten Pfad; DKV-Downloads pruefen den Dateinamen gegen ein DKV_*.xlsx-Muster, apps/api/src/dkv/dkv.controller.ts:134). Es wird kein statisches Verzeichnis veroeffentlicht. Neu ist allein die Lebensdauer |
| T-cx0-02 | Tampering | verworfene Bind-Mount-Variante | medium | mitigate | Benanntes Volume statt Host-Pfad: kein Verzeichnis des Wirts wird in den Container gereicht, kein repo-relativer Pfad legt Nutzerinhalte in die Arbeitskopie. Zusaetzlich bleibt die Eigentuemerschaft uid 1001 aus apps/api/Dockerfile:23-26 erhalten, statt root-eigene Rechte einzufuehren |
| T-cx0-03 | Denial of Service | Plattenbedarf des Volumes | low | accept | Wachstum ist bereits im Code begrenzt: Profilbilder sind auf 2 MB gedeckelt (apps/api/src/user/user.controller.ts:232) und je Nutzer bleibt genau eine Datei (aeltere Endungen werden geloescht, :255-261); DKV-Exporte werden auf zehn Dateien beschnitten (MAX_EXPORT_FILES = 10, apps/api/src/dkv/dkv-export.service.ts:11). Das Volume selbst hat keine Groessengrenze — als Betriebshinweis in Kapitel 6 aufgenommen, keine Codeaenderung |
| T-cx0-04 | Spoofing | CLAUDE.md, Abschnitt Authentifizierung | medium | mitigate | Das Dokument nennt heute einen Identitaetsanbieter, der nicht existiert. Wer das glaubt, nimmt Schutzfunktionen an (Sitzungsverwaltung, Sperren, Verzeichnisfoederation), die in Wahrheit selbst gebaut sind. Task 2 ersetzt die Angabe durch die tatsaechliche Kette aus eigenem JWT, argon2 und ldapts |
| T-cx0-05 | Repudiation | WINDOWS #17 im Ledger | low | mitigate | Der Eintrag wird NICHT geschlossen. Die Reparatur im Repository erreicht die laufende Installation nicht; ein Schliessen wuerde einen Zustand behaupten, der auf alpha nicht vorliegt. Die Zusammenfassung haelt fest, was noch aussteht und wer es tut |
| T-cx0-SC | Tampering | npm/pnpm-Installationen | n/a | accept | Dieser Plan installiert kein Paket und aendert keine Abhaengigkeit. Eine Pruefung der Paketherkunft ist deshalb nicht erforderlich; die zweite automatisierte Pruefung in Task 2 (git diff --exit-code auf alle package.json und pnpm-lock.yaml) erzwingt genau das |
| </threat_model> |
- Das Mount-Tor aus Task 1 — drei gerenderte Konfigurationen plus die Mount-Zeile im Betriebshandbuch. War vor der Arbeit vierfach rot.
- Das Versions-Tor aus Task 2 — zwei Stichproben auf die korrigierten Zahlen plus die Ausschlusspruefung fuer nie eingebaute Technik. War vor der Arbeit dreifach rot.
git diff --exit-codeauf allepackage.jsonundpnpm-lock.yaml— belegt, dass keine Abhaengigkeit angefasst wurde.git status --short— nur die fuenf im Plan genannten Dateien duerfen geaendert sein.
(a) Lokaler Beweis, dass die Dateien ueberleben. Container mit den geaenderten
Compose-Dateien neu erstellen, im Portal unter den eigenen Einstellungen ein Profilbild
hochladen, danach docker compose up -d --force-recreate api, Seite neu laden: das Bild ist
noch da. Gegenprobe frueher: genau hier ging es verloren.
(b) Uebernahme auf alpha. Dieselben zwei Zeilen in /opt/tessera/docker-compose.yml
eintragen (Datei vorher sichern), Container neu erstellen, danach docker inspect tessera-api-1 pruefen — unter Mounts muss /app/user-files erscheinen. Erst wenn das
erledigt ist, darf WINDOWS #17 geschlossen werden (gsd-tools windows fixed 17).
(c) Durchsicht der korrigierten Technik-Tabelle durch den Nutzer, falls gewuenscht — inhaltlich pruefbar ohne Fachkenntnis: es darf nichts drinstehen, was es im Projekt nicht gibt.
<success_criteria>
- Beide automatisierten Tore gruen, beide waren vorher nachweislich rot.
- Genau zwei Commits, in dieser Reihenfolge und jeweils fuer sich lauffaehig:
Commit 1 (Task 1) —
fix(compose): user-files dauerhaft speichern und Betriebshandbuch nachziehen; Commit 2 (Task 2) —docs(claude): Versionsangaben auf den installierten Stand bringen. - Keine Abhaengigkeit aktualisiert, kein Server angefasst, keine Datei ausserhalb des Arbeitsbaums beruehrt.
- WINDOWS #17 bleibt offen; die Zusammenfassung nennt den verbliebenen Schritt des Nutzers woertlich und in Alltagssprache.
- Die Zusammenfassung nennt die gewaehlte Speicherart samt Begruendung und alle beim Nachschlagen gefundenen Abweichungen von der Versionstabelle in diesem Plan. </success_criteria>