phase: 01-core-stack
plan: 02
type: execute
wave: 2
depends_on:
- "01-01"
files_modified:
- docker-compose.yml
- docker-compose.env.template
autonomous: true
requirements:
- STACK-01
- STACK-05
must_haves:
truths:
- "Alle 6 Dienste haben Healthchecks und starten mit restart: unless-stopped"
- "docker compose up -d startet den Stack auf Debian 13 ohne Host-Abhaengigkeiten"
- "docker-compose.env.template existiert als versioniertes Konfigurationstemplate"
- "Alle Dienste kommunizieren ueber ein explizit deklariertes internes Netz"
- "Ollama-Port 11434 ist nur auf 127.0.0.1 gebunden"
artifacts:
- path: "docker-compose.yml"
provides: "Stack mit Healthchecks, explizitem Netz, lokalem Ollama-Binding"
contains: "healthcheck"
- path: "docker-compose.env.template"
provides: "Konfigurationstemplate fuer alle Geheimnisse und Einstellungen"
contains: "PAPERLESS_SECRET_KEY"
key_links:
- from: "docker-compose.yml networks"
to: "alle 6 Dienste"
via: "networks: [paperless-net]"
pattern: "paperless-net"
- from: "docker-compose.env.template"
to: "docker-compose.env (nicht in Git)"
via: "cp docker-compose.env.template docker-compose.env"
pattern: "PAPERLESS_SECRET_KEY"
Den 6-Dienste-Stack produktionsreif machen: explizites internes Docker-Netz, Healthchecks auf allen Diensten, Sicherheits-Haertung (Ollama nur localhost), und ein versioniertes Konfigurationstemplate.
Purpose: Nach Plan 01 sind alle Dienste definiert. Dieser Plan stellt sicher, dass docker compose up -d auf einem frischen Debian-13-System zuverlaessig funktioniert — ohne dass Host-seitige Prozesse laufen muessen.
Output: Gehaertetes docker-compose.yml + docker-compose.env.template.
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-core-stack/01-01-SUMMARY.md
Bekannte Healthcheck-Patterns:
redis: test: ["CMD", "redis-cli", "ping"]
postgres: test: ["CMD-SHELL", "pg_isready -U paperless -d paperless"]
paperless-ngx: test: ["CMD", "curl", "-f", "http://localhost:8000/accounts/login/"]
ollama: test: ["CMD", "curl", "-f", "http://localhost:11434"]
gitea: test: ["CMD", "curl", "-f", "http://localhost:3000"]
paperless-ai: test: ["CMD", "curl", "-f", "http://localhost:3000"]
Healthcheck-Standardwerte:
interval: 30s
timeout: 10s
retries: 3
start_period: 30s (paperless-ngx und ollama: 60s wegen laengerem Startup)
Ollama-Port-Binding Sicherheit:
FALSCH: ports: ["11434:11434"]
RICHTIG: ports: ["127.0.0.1:11434:11434"]
Netz-Name: paperless-net
Netz-Treiber: bridge
- docker-compose.yml — vollstaendigen Stand nach Plan 01 lesen (6 Dienste)
Folgende Aenderungen an docker-compose.yml vornehmen:
1. Netzwerk-Deklaration am Ende der Datei ergaenzen (nach dem volumes-Block):
networks:
paperless-net:
driver: bridge
2. Jeden der 6 Dienste mit networks: [paperless-net] versehen.
3. Healthchecks fuer alle 6 Dienste ergaenzen:
broker (redis):
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
db (postgres):
healthcheck:
test: ["CMD-SHELL", "pg_isready -U paperless -d paperless"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
webserver (paperless-ngx):
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/accounts/login/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
ollama:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
gitea:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
paperless-ai:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
4. Ollama-Port-Binding auf localhost beschraenken (Sicherheit, aus Threat T-01-01):
ports:
- "127.0.0.1:11434:11434"
(statt "11434:11434")
5. depends_on bei webserver auf condition: service_healthy aktualisieren:
depends_on:
db:
condition: service_healthy
broker:
condition: service_healthy
6. depends_on bei paperless-ai auf condition: service_healthy aktualisieren:
depends_on:
webserver:
condition: service_healthy
ollama:
condition: service_healthy
KEIN network_mode: bridge setzen — das wuerde host.docker.internal-Aufloesung brechen.
- grep "paperless-net" docker-compose.yml | wc -l gibt 7 oder mehr aus (1x Definition + 6x Dienste)
- grep "healthcheck:" docker-compose.yml | wc -l gibt genau 6 aus (je ein Healthcheck pro Dienst)
- grep "127.0.0.1:11434:11434" docker-compose.yml findet genau eine Zeile
- grep "11434:11434" docker-compose.yml findet KEINE Zeile ohne "127.0.0.1" davor
- grep "network_mode" docker-compose.yml gibt KEINE Ausgabe (nicht gesetzt — wuerde host.docker.internal brechen)
- docker compose config laeuft ohne Fehler durch (valides YAML)
- docker compose config --services listet exakt 6 Dienste: broker, db, gitea, ollama, paperless-ai, webserver
- docker-compose.yml — welche env_file-Eintraege existieren, welche Variablen werden eingelesen
- .env — aktueller Inhalt (COMPOSE_PROJECT_NAME)
docker-compose.env.template im Projektverzeichnis erstellen. Diese Datei wird in Git versioniert. Die eigentliche docker-compose.env (mit echten Geheimnissen) wird NICHT in Git eingecheckt.
Inhalt:
# docker-compose.env.template
# Kopieren nach docker-compose.env und Werte eintragen:
# cp docker-compose.env.template docker-compose.env
#
# docker-compose.env wird NICHT in Git eingecheckt (.gitignore).
# Alle Pflichtfelder sind mit CHANGE_ME markiert.
# === Paperless-ngx ===
PAPERLESS_SECRET_KEY=CHANGE_ME_generate_with_openssl_rand_hex_32
PAPERLESS_ADMIN_USER=admin
PAPERLESS_ADMIN_PASSWORD=CHANGE_ME
PAPERLESS_ADMIN_MAIL=admin@localhost
# Sprache und Zeitzone
PAPERLESS_OCR_LANGUAGE=deu+eng
PAPERLESS_TIME_ZONE=Europe/Berlin
# URL (fuer Links in E-Mails etc.)
PAPERLESS_URL=http://localhost:8000
# === paperless-ai ===
# API-Token wird nach dem ersten Start generiert:
# docker exec paperless-webserver-1 python3 manage.py shell -c "
# from rest_framework.authtoken.models import Token
# from django.contrib.auth.models import User
# user = User.objects.first()
# token, _ = Token.objects.get_or_create(user=user)
# print(f'Token: {token.key}')
# "
PAPERLESS_API_TOKEN=CHANGE_ME_see_comment_above
PAPERLESS_API_URL=http://webserver:8000/api
PAPERLESS_URL_AI=http://webserver:8000
# KI-Anbieter: ollama (Standard, lokal) | anthropic | openrouter
AI_PROVIDER=ollama
OLLAMA_API_URL=http://ollama:11434
OLLAMA_MODEL=llama3.2
SCAN_INTERVAL=*/30 * * * *
# Cloud-KI (nur benoetigt wenn AI_PROVIDER != ollama)
ANTHROPIC_API_KEY=
OPENROUTER_API_KEY=
# === Gitea ===
# Gitea nutzt SQLite — keine weiteren DB-Variablen noetig
# Erstzugang: http://localhost:3001 nach dem ersten Start
# === PostgreSQL (Paperless-interne DB) ===
# Passwort aus docker-compose.yml hierher verschieben (Sicherheit):
POSTGRES_PASSWORD=CHANGE_ME
Sicherstellen, dass docker-compose.env in .gitignore eingetragen ist (falls .gitignore noch nicht existiert, erstellen).
Pruefen ob POSTGRES_PASSWORD bereits in docker-compose.yml als Klartext steht. Falls ja: einen Kommentar-Hinweis in die Datei schreiben, dass der Wert in Phase 3 via SCRIPT-03 aus docker-compose.env eingelesen werden soll. Den Klartext-Wert in docker-compose.yml JETZT noch NICHT entfernen (das erledigt SCRIPT-03 in Phase 3 — der Stack muss erstmal laufen).
- test -f docker-compose.env.template gibt Exit-Code 0 zurueck
- grep "PAPERLESS_SECRET_KEY" docker-compose.env.template findet mindestens eine Zeile
- grep "PAPERLESS_API_TOKEN=CHANGE_ME" docker-compose.env.template findet mindestens eine Zeile
- grep "AI_PROVIDER=ollama" docker-compose.env.template findet die Standardkonfiguration
- grep "OLLAMA_API_URL=http://ollama:11434" docker-compose.env.template bestaetigt internes Netz (nicht host.docker.internal)
- grep "docker-compose.env" .gitignore findet einen Eintrag (Geheimnisse nicht in Git)
- test ! -f docker-compose.env bestaetigt dass die echte .env-Datei nicht existiert (nur Template)
Vertrauensgrenzen
| Grenze | Beschreibung |
|---|---|
| docker-compose.env (Geheimnisse) → Git | Muss durch .gitignore blockiert sein |
| Ollama-API Port 11434 → Host-Netz | Nach Haertung: nur 127.0.0.1 sichtbar |
| paperless-ngx Webinterface :8000 → lokales Netz | Kein TLS, kein Reverse-Proxy in v0.1 |
STRIDE Bedrohungsregister
| Threat ID | Kategorie | Komponente | Disposition | Massnahme |
|---|---|---|---|---|
| T-02-01 | Information Disclosure | docker-compose.env mit Geheimnissen | mitigate | .gitignore-Eintrag zwingend (Teil von Task 2); Template-Datei ohne echte Werte in Git |
| T-02-02 | Tampering | Healthcheck-Kommandos koennen fehlschlagen wenn curl nicht im Image | accept | paperless-ngx- und Gitea-Images enthalten curl; Ollama-Image enthaelt curl; Redis und Postgres nutzen native Kommandos |
| T-02-03 | Denial of Service | Fehlkonfiguriertes depends_on: service_healthy blockiert Start | accept | start_period-Werte ausreichend gross (60s fuer Ollama/Paperless); Monitoring via docker compose ps |
| T-02-04 | Information Disclosure | POSTGRES_PASSWORD als Klartext in docker-compose.yml | mitigate | Template dokumentiert Migration nach docker-compose.env; SCRIPT-03 (Phase 3) entfernt Klartext aus Compose |
| T-02-05 | Spoofing | Kein Netz-Isolation zwischen Diensten im selben paperless-net | accept | Single-Tenant-Setup; alle Dienste gehoeren zum selben Stack; keine Mandantentrennung noetig |
Gesamt-Verifikation nach Plan 02:
# Stack-Validierung
docker compose -f /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml config
# Dienste-Liste
docker compose -f /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml config --services
# Erwartet: broker, db, gitea, ollama, paperless-ai, webserver
# Healthchecks vorhanden
grep -c "healthcheck:" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml
# Erwartet: 6
# Netz deklariert
grep "paperless-net" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml | wc -l
# Erwartet: >= 7
# Ollama nur auf localhost
grep "127.0.0.1:11434" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml
# Erwartet: eine Zeile
# Template vorhanden
test -f /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.env.template && echo "OK"
# Geheimnisse nicht in Git
grep "docker-compose.env" /Users/bmt/Documents/OhnePapier-v.0.0/.gitignore
- docker compose config validiert ohne Fehler
- Alle 6 Dienste sind im paperless-net
- Alle 6 Dienste haben Healthchecks
- Ollama lauscht nur auf 127.0.0.1:11434 (nicht 0.0.0.0)
- docker-compose.env.template im Projektverzeichnis versioniert
- docker-compose.env ist in .gitignore eingetragen
- Template enthaelt OLLAMA_API_URL=http://ollama:11434 (internes Netz, nicht host.docker.internal)