🛬 Ingest-Pipeline — MVP

Ziel: Kollegen sollen Quellen (Dateien, URLs) ins Wiki einreichen können, ohne Git, Obsidian, Kommandozeile oder Markdown zu kennen. Die Datei landet automatisch in raw/.inbox/, die eigentliche Einarbeitung macht vorerst weiterhin Martin + Claude von Hand.

Scope-Abgrenzung

Der MVP löst nur das Eingabe-Problem. Der autonome Ingest-Worker (Claude Agent SDK, automatische Wiki-Bearbeitung, Commits) ist Phase 2 und bewusst nicht Teil des MVP.


Das Gesamtbild (alle Phasen)

  ┌──────────────────────────────────────────────┐
  │  Eingabe                                      │
  │  ┌─────────┐  ┌─────────┐  ┌─────────┐       │
  │  │Drag&Drop│  │URL-Form │  │  Mail   │       │  ← Phase 1 (MVP): Drag&Drop + URL
  │  └────┬────┘  └────┬────┘  └────┬────┘       │     Phase 3: Mail-Eingang
  └───────┼────────────┼────────────┼─────────────┘
          ▼            ▼            ▼
  ┌──────────────────────────────────────────────┐
  │  ingest-app (FastAPI, Port 8081)             │  ← Phase 1 (MVP)
  │  POST /upload → schreibt nach raw/.inbox/    │
  └───────────────────┬──────────────────────────┘
                      ▼
  ┌──────────────────────────────────────────────┐
  │  raw/.inbox/                                  │  ← bleibt immer Zwischenlager
  │  2026-04-17_foo.pdf                           │
  │  2026-04-17_bar.url.md                        │
  └───────────────────┬──────────────────────────┘
                      ▼
  ┌──────────────────────────────────────────────┐
  │  Ingest-Verarbeitung                         │
  │  • Phase 1: Martin ruft Claude manuell auf   │
  │  • Phase 2: Autonomer Worker (Agent SDK)     │
  └───────────────────┬──────────────────────────┘
                      ▼
  ┌──────────────────────────────────────────────┐
  │  content/ + commit + quartz rebuild          │
  └──────────────────────────────────────────────┘

MVP: was wir wirklich bauen

Was der Nutzer sieht

Eine einzige Seite unter http://joule-wiki.local:8081/upload:

  • großes Drop-Feld („Datei hier ablegen — PDF, DOCX, PPTX, XLSX, MD, TXT, PNG”)
  • darunter ein URL-Feld („… oder URL einkleben”)
  • Pflicht-Textfeld „Worum geht’s? Wo kommt das her?” — 1–3 Sätze, wird als .md-Kontextnotiz neben der Datei abgelegt. Ohne Ausfüllen keine Einreichung.
  • Button „Einreichen” (disabled, solange Kontextfeld leer oder weniger als ~10 Zeichen)
  • nach Erfolg: kurze Bestätigung mit Link zurück zum Wiki und dem vergebenen Dateinamen

Kein Login. Das Tool läuft im ADVIA-Intranet unter http://joule-wiki.local:8081/upload (später hinter Reverse Proxy mit SSO — explizit Phase 2+, nicht MVP).

Was im Hintergrund passiert

  1. Datei-Upload: Browser schickt Multipart-POST an /upload.
  2. URL-Eingabe: Server legt eine JJJJ-MM-TT_<slug>.url.md in raw/.inbox/ an. Inhalt:
    ---
    quelle_url: https://...
    eingereicht_von: <ip oder cookie-name>
    eingereicht_am: 2026-04-17T09:12:00+02:00
    kontext: "<vom Nutzer eingegeben, optional>"
    ---
    # <domain> — <pfad>
    
    Kein HTML-Snapshot im MVP — das macht die eigentliche Ingest-Verarbeitung später, wenn überhaupt nötig.
  3. Datei-Upload: Server normalisiert den Dateinamen zu JJJJ-MM-TT_<original-slug>.<ext>, legt ihn in raw/.inbox/ ab. Bei Kollision: _2, _3, … anhängen.
  4. Kontext-Notiz (Pflicht): Eine gleichnamige .md-Datei wird daneben gelegt mit dem Text aus dem Kontextfeld (Konvention laut Raw-Anleitung). Format:
    ---
    eingereicht_von: <ip oder cookie-name>
    eingereicht_am: 2026-04-17T09:12:00+02:00
    ---
    <Text aus dem Kontextfeld>
    
  5. Server-Validierung: Ohne Kontext-Text (<10 Zeichen) antwortet /upload mit 400 Bad Request — doppelter Schutz, falls jemand das Frontend umgeht.
  6. Response: 200 + HTML-Bestätigung. Fertig.

Was der MVP nicht kann (bewusst)

  • Keine Authentifizierung. Läuft nur im Intranet.
  • Keine Virenprüfung. Wer zugang zum Intranet hat, kann ohnehin Dateien ablegen.
  • Kein Mail-Eingang. Phase 3.
  • Keine automatische Wiki-Bearbeitung. Claude wird weiterhin manuell aufgerufen („Integriere die neuen Dateien aus raw/.inbox/ ins Wiki”).
  • Kein Quartz-Rebuild-Trigger. Der Rebuild passiert erst nach der Claude-Verarbeitung, nicht nach dem Upload.
  • Kein Mehrmandantenfähigkeit, kein Rechtesystem, kein Admin-Panel.

Technik

Stack

  • FastAPI (Python 3.12, uvicorn) — gerade genug, in ~80 Zeilen fertig.
  • Single HTML-Template mit Vanilla-JS (kein React, kein Build-Step).
  • Läuft als dritter Docker-Service neben wiki und wiki-dev.

docker-compose.yml — Ergänzung

services:
  # ... wiki und wiki-dev bleiben unverändert ...
 
  ingest-app:
    build: ./ingest-app
    ports:
      - "8081:8081"
    volumes:
      - ./raw/.inbox:/app/inbox
    restart: unless-stopped

Verzeichnisstruktur

joule-wiki/
├── content/              ← Wiki-Inhalt (unverändert)
├── raw/
│   └── .inbox/           ← ingest-app schreibt hier rein
├── ingest-app/           ← NEU
│   ├── Dockerfile
│   ├── requirements.txt
│   ├── app.py            ← FastAPI-App (~80 Zeilen)
│   └── templates/
│       └── upload.html
└── docker-compose.yml    ← ingest-app-Service ergänzen

Minimal-Endpunkte

MethodePfadZweck
GET/Redirect auf /upload
GET/uploadHTML-Formular (Drop-Zone + URL-Feld)
POST/uploadNimmt Datei oder URL entgegen
GET/healthHealth-Check (für Docker healthcheck)

Sicherheits-Minimum

  • Dateigröße hart limitieren (z.B. 50 MB) — gegen Versehen.
  • Whitelist erlaubter MIME-Types (PDF, DOCX, PPTX, XLSX, MD, TXT, PNG, JPG, MP3, MP4, URL-Shortcuts).
  • Zeichensatz-Normalisierung des Dateinamens (keine ../-Tricks).
  • Kein Ausführen, kein Entpacken, kein Parsen — die Datei wird nur abgelegt.

Fahrplan

SchrittWasStatus
1ingest-app/ erstellen (Dockerfile, FastAPI-App)✅ 2026-04-17
2docker-compose.yml-Erweiterung✅ 2026-04-17
3HTML-Formular (Drop-Zone, URL-Feld, Pflicht-Kontext)✅ 2026-04-17
4Smoke-Tests (PDF, URL, Kollisionen, Validierung)✅ 2026-04-17 (10/10)
5Lokaler Build + Testlauf auf Martins Rechner🔄 offen
6Intranet-Freigabe besprechen (Firewall, DNS-Alias)🔄 offen (mit IT)

Starten & testen

# Nur die Ingest-App:
docker compose up --build ingest-app
# → http://localhost:8081/upload
 
# Zusammen mit dem Wiki:
docker compose up --build wiki ingest-app
# → Wiki: http://localhost:8080
# → Upload: http://localhost:8081/upload

Health-Check:

curl http://localhost:8081/health
# { "status": "ok", "inbox": "/app/inbox", "exists": "True" }

Eingereichte Dateien erscheinen sofort in raw/.inbox/ neben ihrer .context.md-Datei. Nächster Schritt wie gehabt: Claude bitten, raw/.inbox/ einzuarbeiten.

Phase 2 (optional, später)

  • Autonomer Ingest-Worker: Python-Service, der raw/.inbox/ beobachtet (watchdog), pro neuer Datei einen Claude-Agent-SDK-Lauf startet, CLAUDE.md als System-Prompt mitgibt, und bei Erfolg einen Git-Commit auf main pusht. Voraussetzungen: Anthropic-API-Budget, Commit-Rechte, DSGVO-Clearing.
  • Quartz-Rebuild-Trigger: Nach jedem Commit ein webhook-basierter Rebuild des wiki-Containers.
  • Status-Feedback: Der Uploader bekommt per Mail (oder Slack) Bescheid, wenn seine Quelle im Wiki gelandet ist — mit Link auf die erzeugten/aktualisierten Seiten.

Phase 3 (optional, später)

  • Mail-Eingang: IMAP-Polling eines Postfachs ingest@joule-wiki.aequitas.de, Anhänge landen automatisch in raw/.inbox/, Mailbody wird zur Kontext-Notiz.

Entscheidungen (festgezurrt am 2026-04-17)

  • DNS-Aliasjoule-wiki.local im Intranet. Kein öffentliches DNS, kein wiki.aequitas.de im MVP.
  • Authentifizierung → bewusst keine im MVP. Vertrauen ins Intranet ist ausreichend, bis das Tool außerhalb läuft. SSO/Reverse-Proxy ist Phase 2.
  • Kontext-FeldPflicht. Mindestens ~10 Zeichen, Frontend disabled den Einreichen-Button bis befüllt, Backend validiert zusätzlich. Grund: spart später Rückfragen bei der Ingest-Verarbeitung und dokumentiert die Quelle beim Einreichen — also genau dann, wenn der Kontext noch frisch ist.

← Meta · Inhaltsverzeichnis · Raw-Anleitung