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


Task 1: Explizites Netz + Healthchecks + Sicherheits-Haertung in docker-compose.yml
docker-compose.yml

- 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 -n "paperless-net" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml | wc -l && grep -n "healthcheck" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml | wc -l && grep -n "127.0.0.1:11434" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml && docker compose -f /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.yml config 2>&1 | tail -5


- 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

Alle 6 Dienste im paperless-net; alle haben Healthchecks; Ollama nur auf 127.0.0.1; docker compose config validiert sauber


Task 2: docker-compose.env.template erstellen
docker-compose.env.template

- 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 /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.env.template && grep -c "CHANGE_ME" /Users/bmt/Documents/OhnePapier-v.0.0/docker-compose.env.template && grep "docker-compose.env" /Users/bmt/Documents/OhnePapier-v.0.0/.gitignore 2>/dev/null || echo "GITIGNORE_CHECK_NEEDED"


- 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)

docker-compose.env.template versioniert mit allen Pflichtfeldern; docker-compose.env in .gitignore; Template nutzt http://ollama:11434 statt host.docker.internal

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)

Nach Abschluss `.planning/phases/01-core-stack/01-02-SUMMARY.md` erstellen.