🛬 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
- Datei-Upload: Browser schickt Multipart-POST an
/upload. - URL-Eingabe: Server legt eine
JJJJ-MM-TT_<slug>.url.mdinraw/.inbox/an. Inhalt:
Kein HTML-Snapshot im MVP — das macht die eigentliche Ingest-Verarbeitung später, wenn überhaupt nötig.--- 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> - Datei-Upload: Server normalisiert den Dateinamen zu
JJJJ-MM-TT_<original-slug>.<ext>, legt ihn inraw/.inbox/ab. Bei Kollision:_2,_3, … anhängen. - 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> - Server-Validierung: Ohne Kontext-Text (<10 Zeichen) antwortet
/uploadmit400 Bad Request— doppelter Schutz, falls jemand das Frontend umgeht. - 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
wikiundwiki-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-stoppedVerzeichnisstruktur
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
| Methode | Pfad | Zweck |
|---|---|---|
| GET | / | Redirect auf /upload |
| GET | /upload | HTML-Formular (Drop-Zone + URL-Feld) |
| POST | /upload | Nimmt Datei oder URL entgegen |
| GET | /health | Health-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
| Schritt | Was | Status |
|---|---|---|
| 1 | ingest-app/ erstellen (Dockerfile, FastAPI-App) | ✅ 2026-04-17 |
| 2 | docker-compose.yml-Erweiterung | ✅ 2026-04-17 |
| 3 | HTML-Formular (Drop-Zone, URL-Feld, Pflicht-Kontext) | ✅ 2026-04-17 |
| 4 | Smoke-Tests (PDF, URL, Kollisionen, Validierung) | ✅ 2026-04-17 (10/10) |
| 5 | Lokaler Build + Testlauf auf Martins Rechner | 🔄 offen |
| 6 | Intranet-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/uploadHealth-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 aufmainpusht. 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 inraw/.inbox/, Mailbody wird zur Kontext-Notiz.
Entscheidungen (festgezurrt am 2026-04-17)
- DNS-Alias →
joule-wiki.localim Intranet. Kein öffentliches DNS, keinwiki.aequitas.deim 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-Feld → Pflicht. 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.