No description
  • Python 90.5%
  • JavaScript 7.3%
  • CSS 1.6%
  • HTML 0.3%
  • Dockerfile 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Stefan Lindecke 275332251d fix(compose): skip locally built app image on docker compose pull
bookclaw-app:dev only exists locally, so `docker compose pull` failed with
"pull access denied". All app services now set `pull_policy: build`:
pull only refreshes postgres/redis/ollama, `up` rebuilds the app image
from the build cache. README documents the day-to-day Docker calls
(status, readyz, logs, first-user registration, stop, pull/rebuild, QA)
and the one-time Ollama model pulls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:24:00 +02:00
.github/workflows build: lock dependencies with uv.lock for Docker and CI 2026-09-25 15:17:15 +02:00
docs feat(auth): login system with User/Admin roles, project ownership, full project deletion 2026-09-08 15:32:38 +02:00
frontend feat(frontend): sidebar layout with workflow-grouped navigation 2026-09-25 15:17:15 +02:00
migrations feat(auth): login system with User/Admin roles, project ownership, full project deletion 2026-09-08 15:32:38 +02:00
src/bookclaw feat(worker): configurable Redis socket and queue poll timeouts 2026-09-25 15:17:15 +02:00
tests feat(worker): configurable Redis socket and queue poll timeouts 2026-09-25 15:17:15 +02:00
.dockerignore build: lock dependencies with uv.lock for Docker and CI 2026-09-25 15:17:15 +02:00
.env.example feat(worker): configurable Redis socket and queue poll timeouts 2026-09-25 15:17:15 +02:00
.gitignore feat(foundation): Epic 1 – project skeleton, FastAPI, config, DB/Redis, healthchecks, CI 2026-09-07 09:33:24 +02:00
agents.md feat(project,source): Epic 2 – Project CRUD, immutable Source import, blob storage 2026-09-07 09:55:49 +02:00
alembic.ini feat(foundation): Epic 1 – project skeleton, FastAPI, config, DB/Redis, healthchecks, CI 2026-09-07 09:33:24 +02:00
compose.yaml fix(compose): skip locally built app image on docker compose pull 2026-09-25 15:24:00 +02:00
Dockerfile build: lock dependencies with uv.lock for Docker and CI 2026-09-25 15:17:15 +02:00
LICENSE feat(foundation): Epic 1 – project skeleton, FastAPI, config, DB/Redis, healthchecks, CI 2026-09-07 09:33:24 +02:00
pyproject.toml feat(auth): login system with User/Admin roles, project ownership, full project deletion 2026-09-08 15:32:38 +02:00
README.md fix(compose): skip locally built app image on docker compose pull 2026-09-25 15:24:00 +02:00
uv.lock build: lock dependencies with uv.lock for Docker and CI 2026-09-25 15:17:15 +02:00

BookClaw

Personal Book Operating System – KI-gestütztes, selbst hostbares System zur Entwicklung von Büchern aus gesprochenen Gedanken, Interviews, Notizen und Dokumenten.

Der Mensch erzählt ungeordnet. BookClaw hilft beim Erinnern, Ordnen, Prüfen, Strukturieren und Schreiben, ohne fehlende Fakten zu erfinden.

Status

Frühe Entwicklung. Aktuell umgesetzt:

  • Epic 1 – Foundation: Repo-Grundgerüst, FastAPI, Konfiguration, PostgreSQL/Alembic, Redis, Healthchecks, CI.
  • Epic 2 – Project + Source: Project CRUD, BlobStorage-Abstraktion (lokal), Upload mit Streaming-SHA-256-Hashing, MIME-/Größenvalidierung, Source Immutability.
  • Epic 3 – Transcription: ProcessingRun-Audit-Trail, versionierte Transcript/ TranscriptSegment-Domäne, TranscriptionProvider-Abstraktion mit Faster-Whisper-Adapter (optionale Extra-Dependency), Redis-Job-Queue, Worker-Entrypoint, idempotente Transkriptions-Requests.
  • Epic 4 – Knowledge: Claim/Memory/Person/Place/Topic-Domäne mit Provenance (SourceReference bis auf Transkript-Segment-Ebene), Memory-/Entity-Agent mit strukturierter LLM-Extraktion (Ollama-Adapter, Schema-Validierung + begrenzter Repair-Retry), Confirm/Reject-Workflow für Claims (nie automatisch aus Confidence abgeleitet), Volltext- und semantische Suche (pgvector), Herkunftsprüfung (UC-10).
  • Epic 5 – Timeline: TemporalRange/PartialDate mit präzisionsehrlicher Validierung (nie erfundene Monats-/Tagesangaben), Event-Domäne, Timeline-Agent als dritter Agent im Extraction-Run, Contradiction-Domäne mit menschlichem Resolution-Workflow (nie automatisch aus Confidence entschieden), Embedding-basierte Kandidatenvorauswahl für die Widerspruchserkennung (UC-06, UC-07).
  • Epic 6 – Interview: Question-Domäne (exponiert als "interviews"-Ressource gemäß Architecture-API-Sketch), Interview Agent mit Gap-/Wiederholungs-bewusstem Kontext (bekannte Entitäten, abgedeckte Jahre, alle bisherigen Fragen), "eine offene Frage gleichzeitig" ohne separates Sperr-Feld, typisierte Antworten laufen als verbatim Source/Transcript durch dieselbe Provenienzkette wie gesprochene und stoßen automatisch die Extraktion an (UC-04).
  • Epic 7 – Outline + Writer: Outline-Aggregat (Book → BookPart → Chapter → Scene) mit Structure Agent, der Titel/Zusammenfassungen aus vorhandenem Material vorschlägt, aber bewusst keine Claim-IDs errät (UC-08); eine Outline bleibt proposed bis der Mensch sie explizit bestätigt (Human-in-the-Loop, erst dann darf ein Kapitel entworfen werden); Writer Agent erzeugt quellengebundene Kapitelentwürfe aus per semantischer Suche gefundenen Claims, filtert dabei jede vom Modell zurückgemeldete, aber nicht tatsächlich angebotene Claim-ID defensiv heraus (UC-09); jeder Entwurf ist eine neue, nie überschriebene ManuscriptRevision.
  • Epic 8 – Export: vollständiges Projektarchiv als ein Zip aus reinem YAML/Markdown — Projekt, Sources-Manifest (Originalquellen optional per Flag einschließbar), Transkripte, das gesamte Knowledge-Modell (Claims/Memories/Personen/Orte/Themen/Timeline/ Contradictions), Interviews, Outline und Manuskript (je Kapitel die neueste Revision als Markdown), plus der komplette ProcessingRun-Audit-Trail (UC-11); braucht als einziger Job-Typ keinen AI-Gateway-Aufruf — reine deterministische Serialisierung aus bereits vorhandenen Daten, mit unzip und einem Texteditor lesbar, keine proprietäre BookClaw-Installation zum Lesen nötig (Lastenheft §14).
  • Project Agent (UC-01): ProjectProposal-Domäne, aus einem frei formulierten Ideentext vorgeschlagen (Titel, Buchtyp, Themen, Zielgruppe, Erzählstimme, eine grobe Kapitel-Titel-Liste, offene Annahmen/Fragen) — läuft bewusst vor jedem Project (docs/ARCHITECTURE.md §17), wofür ProcessingRun.project_id als einzige Ausnahme in der ganzen Codebase nullable ist; „AI Proposal ist nicht direkt das persistierte Projekt ohne Bestätigung" — erst eine explizite Bestätigung erzeugt das echte Project.
  • Web-UI, alle zwölf Hauptbereiche (Lastenheft §15): frontend/, reines HTML/CSS/JS ohne Build-Schritt oder npm-Abhängigkeit, als Hash-Router-SPA (frontend/js/router.js + frontend/js/views/*.js), von bookclaw dev/serve unter / mitausgeliefert. Dashboard (Statistiken + Projekt anlegen, sowohl manuell als auch über UC-01s Ideen-Flow), Aufnahme/Import (MediaRecorder-Aufnahme direkt im Browser + Datei-Import), Inbox (Erinnerungen + Claim-Bestätigung), Timeline, Personen/Orte/Themen, Interview (geführter Ein-Fragen-Flow), Offene Fragen (Übersicht/Audit über alle Fragen), Widersprüche, Outline, Manuskript, Quellen und Einstellungen (Projekt bearbeiten, KI-Modelle & Provider je Projekt auswählen, Export). Durchgängige visuelle Trennung von KI-Vorschlägen/Unsicherheit von bestätigten Fakten (Developer Guidelines §31); kein innerHTML im gesamten Frontend — jeder API-Text kommt über createElement/textContent. Dazu ein dreizehnter, nicht in Lastenheft §15 aufgeführter Bereich "Warteschlange": Länge/Inhalt der Redis-Job-Queue sowie alle Verarbeitungsvorgänge des aktuellen Projekts inkl. Fehlermeldung — die erste Anlaufstelle für "warum passiert nichts". Details und bewusste Auslassungen in frontend/README.md.
  • Login, Rollen, Projekt-Löschung (Lastenheft §23): Registrierung/Login/Logout über ein httpOnly-JWT-Session-Cookie (auth/, api/v1/auth.py), Rollen user/admin (der erste je registrierte User wird automatisch Admin), jedes Project trägt einen owner_user_id — jede weitere Tabelle hängt bereits an project_id, sodass jede Eingabe transitiv auf einen User zurückführbar ist. Autorisierung (api/authorization.py): Projekt-Zugriff nur für den Besitzer oder einen Admin, sonst 403. Settings-UI: "Projekte löschen" — paginierte Liste mit Checkboxen, Bulk-Löschung entfernt ein Projekt vollständig inkl. aller gespeicherten Daten (Postgres ON DELETE CASCADE durch die gesamte Objekthierarchie plus explizites Löschen der zugehörigen Blobs). Bekannte, bewusste Lücke: Autorisierung sitzt auf Projekt-Ebene; Endpunkte, die eine Unterressource direkt per ID adressieren (GET /sources/{id} o. Ä.), prüfen den Besitz noch nicht — siehe docs/PROJEKT_OK.md.

Bewusst noch nicht enthalten (spätere Epics/Roadmap): die S3-Storage-Implementierung (Protokoll steht, LocalBlobStorage ist die einzige konkrete Implementierung), Sprechererkennung/Cloud-STT (Lastenheft §9, "Später"), manuelles Bearbeiten des Manuskripttexts selbst (jede Revision entsteht bisher nur über den Writer Agent), eine Merge-UI für Personen/Orte/Themen, und DOCX/EPUB/PDF-Export (Lastenheft §14 "Später").

API (Auszug)

POST   /api/v1/auth/register  (E-Mail+Passwort; der erste je registrierte User wird Admin)
POST   /api/v1/auth/login     (setzt das httpOnly-Session-Cookie)
POST   /api/v1/auth/logout
GET    /api/v1/auth/me

GET    /api/v1/users                  (Admin-only: alle User)
PATCH  /api/v1/users/{id}/role        (Admin-only: Rolle user/admin setzen)

POST   /api/v1/project-proposals              (UC-01, freier Ideentext, 202 Accepted)
GET    /api/v1/project-proposals
GET    /api/v1/project-proposals/{id}
POST   /api/v1/project-proposals/{id}/confirm (Human-in-the-Loop -> erzeugt das Project)

POST   /api/v1/projects
GET    /api/v1/projects
GET    /api/v1/projects/{id}
PATCH  /api/v1/projects/{id}                  (title/description/privacy_profile/model_configuration —
                                                nur gesendete Felder ändern sich; model_configuration ersetzt
                                                als Ganzes: Pro-Projekt-Override der Whisper/KI-Provider+Modelle,
                                                siehe "KI-Modelle & Provider" im Settings-UI)
GET    /api/v1/projects/paginated?limit=&offset= (Settings "Projekte löschen"-Liste, nur eigene Projekte)
DELETE /api/v1/projects               (Body: {"project_ids": [...]}; vollständige Löschung inkl. Daten)

POST   /api/v1/projects/{id}/sources   (multipart upload)
GET    /api/v1/projects/{id}/sources
GET    /api/v1/sources/{id}

POST   /api/v1/sources/{id}/transcriptions   (enqueue, 202 Accepted)
GET    /api/v1/sources/{id}/transcripts      (alle Versionen)
GET    /api/v1/transcripts/{id}
GET    /api/v1/processing-runs/{id}          (Status pollen)
GET    /api/v1/projects/{id}/processing-runs (alle Verarbeitungsvorgänge dieses Projekts)
GET    /api/v1/queue                         (Diagnose: Länge + Inhalt der Redis-Warteschlange)

POST   /api/v1/transcripts/{id}/extractions  (enqueue, 202 Accepted)
GET    /api/v1/projects/{id}/claims
GET    /api/v1/projects/{id}/claims/search?q=...&mode=fulltext|semantic
GET    /api/v1/claims/{id}
GET    /api/v1/claims/{id}/sources           (Herkunft, UC-10)
POST   /api/v1/claims/{id}/confirm
POST   /api/v1/claims/{id}/reject
GET    /api/v1/projects/{id}/memories
GET    /api/v1/memories/{id}
GET    /api/v1/projects/{id}/{people,places,topics}

GET    /api/v1/projects/{id}/timeline                    (UC-06, sortiert; unsicher zuletzt)
POST   /api/v1/projects/{id}/contradictions/detection     (enqueue, 202 Accepted)
GET    /api/v1/projects/{id}/contradictions
GET    /api/v1/contradictions/{id}
POST   /api/v1/contradictions/{id}/resolution

POST   /api/v1/projects/{id}/interviews      (enqueue next question, 202 Accepted)
GET    /api/v1/projects/{id}/interviews      (alle Fragen des Projekts)
GET    /api/v1/interviews/{id}
POST   /api/v1/interviews/{id}/answers       (Text-Antwort -> Source/Transcript -> Extraction)
POST   /api/v1/interviews/{id}/skip

POST   /api/v1/projects/{id}/outlines        (enqueue Outline-Vorschlag, 202 Accepted)
GET    /api/v1/projects/{id}/outlines        (aktuelle Outline)
GET    /api/v1/outlines/{id}
POST   /api/v1/outlines/{id}/confirm         (Human-in-the-Loop, UC-08)

POST   /api/v1/chapters/{id}/drafts          (enqueue Kapitelentwurf, 202 Accepted, UC-09)
GET    /api/v1/chapters/{id}/drafts          (alle Revisionen dieses Kapitels)
GET    /api/v1/manuscript-revisions/{id}

POST   /api/v1/projects/{id}/exports         (enqueue Projektarchiv, 202 Accepted, UC-11)
GET    /api/v1/projects/{id}/exports         (alle Exports dieses Projekts)
GET    /api/v1/exports/{id}
GET    /api/v1/exports/{id}/download         (Zip-Download)

Dokumentation

Diese drei Spezifikationsdokumente sind bei jeder Weiterentwicklung verbindlich zu beachten (siehe DEVELOPER_GUIDELINES.md §35, Konfliktreihenfolge: Datenintegrität/Privacy > Lastenheft > Architecture > Guidelines > lokale Implementierungsbequemlichkeit).

Lokale Entwicklung

Der Manuskripteditor (frontend/) wird vom API-Prozess unter http://localhost:8000/ mitausgeliefert — kein separates Setup, keine CORS-Konfiguration nötig. OpenAPI-Doku unter /docs.

Healthchecks:

  • GET /api/v1/healthz – Liveness (keine Abhängigkeiten)
  • GET /api/v1/readyz – Readiness (prüft PostgreSQL und Redis)

Variante A – alles in Docker (kein Python/uv auf dem Host)

Voraussetzungen: nur Docker/Podman mit Compose.

git clone <repo-url>
cd bookclaw
docker compose up -d --build          # postgres, redis, migrate, api, worker

API danach unter http://localhost:8000. api und worker teilen sich ein Image (Dockerfile); migrate (alembic upgrade head) läuft als einmaliger Service vor beiden. ./src und ./frontend sind ins Image gemountet, bookclaw dev lädt Änderungen per Autoreload — für Code-Edits ist kein Rebuild nötig (nur bei geänderten Dependencies: docker compose build).

Python-Abhängigkeiten sind über uv.lock reproduzierbar fixiert; Docker und CI verwenden das Lockfile im Locked-Modus. Nach einer Änderung an pyproject.toml deshalb uv lock ausführen und das aktualisierte uv.lock mit committen.

Konfiguration kommt aus dem environment:-Block in compose.yaml (Hostnamen zeigen auf die Compose-Services, nicht localhost). Eine .env auf dem Host wird von den Containern nicht gelesen.

Stack prüfen und manuell testen

docker compose ps                           # api/worker "Up", postgres/redis "healthy"
curl -s localhost:8000/api/v1/readyz        # {"status":"ok", ...} für PostgreSQL und Redis
docker compose logs -f api worker           # Logs mitlesen, z. B. wenn ein KI-Job hängt

Im Browser http://localhost:8000/ öffnen und registrieren — der erste registrierte User wird automatisch Admin. Danach ein Projekt anlegen und die Bereiche durchklicken; für Outline, Writer und Interview muss Ollama mit Modellen laufen (siehe unten).

Stoppen:

docker compose down                         # Daten bleiben in den Volumes erhalten
docker compose down -v                      # inkl. Datenbank- und Blob-Volumes löschen

Images aktualisieren

bookclaw-app:dev wird nur lokal gebaut und liegt in keiner Registry. Die App-Services haben deshalb pull_policy: build: docker compose pull aktualisiert nur die fremden Images (postgres, redis, ollama) und überspringt das eigene, up baut es bei Bedarf aus dem Build-Cache neu.

docker compose pull                         # postgres/redis/ollama aktualisieren
docker compose up -d --build                # eigenes Image neu bauen und starten

Automatische Tests

QS im selben Image (Profil qa, erreicht Postgres/Redis über das Compose-Netz):

docker compose run --rm qa                  # pytest (volle Suite)
docker compose run --rm qa ruff check .
docker compose run --rm qa ruff format --check .
docker compose run --rm qa mypy src

Transkription (faster-whisper) ist im Standard-Image bereits enthalten (INSTALL_SPEECH: "1" im x-bookclaw-build-Anchor in compose.yaml, für alle vier App-Services identisch, da sie sich einen Image-Tag teilen). Für einen schlankeren Build ohne Transkription den Wert dort auf "0" setzen und neu bauen.

Variante B – uv auf dem Host, nur Infra in Docker

Voraussetzungen: Docker/Podman, uv, Python 3.13+.

git clone <repo-url>
cd bookclaw
cp .env.example .env
docker compose up -d postgres redis
uv sync --extra dev
alembic upgrade head
uv run bookclaw dev

Worker starten (verarbeitet u.a. Transkriptions-Jobs aus der Redis-Queue):

uv sync --extra dev --extra speech  # faster-whisper ist eine optionale Extra
uv run bookclaw worker

Ohne die speech-Extra läuft alles andere normal weiter; nur die tatsächliche Modell-Inferenz schlägt mit einer klaren Fehlermeldung fehl (pip install 'bookclaw[speech]').

Optionaler lokaler Ollama-Provider (beide Varianten)

docker compose --profile ollama up -d ollama

Einmalig die Standardmodelle laden (BOOKCLAW_EXTRACTION_MODEL/WRITER_MODEL und BOOKCLAW_EMBEDDING_MODEL); sie liegen danach im Volume bookclaw_ollama_data:

docker compose exec ollama ollama pull llama3.1
docker compose exec ollama ollama pull nomic-embed-text

Externe Provider auf dem Host (LM Studio o. Ä.), außerhalb von Docker

Statt des ollama-Compose-Service kann api/worker (Variante A) einen beliebigen Server ansprechen, der auf derselben Maschine, aber außerhalb von Docker läuft — z. B. LM Studio. Zwei getrennte Themen:

Whisper (Transkription) ist kein Netzwerk-Provider — faster-whisper läuft als eingebettete Python-Bibliothek im worker-Prozess selbst, und LM Studio bietet ohnehin keine Sprach-zu-Text-Funktion. Um faster-whisper außerhalb von Docker laufen zu lassen (z. B. für GPU-Zugriff), den Worker per Variante B auf dem Host starten (uv run bookclaw worker, mit der speech-Extra) — er verbindet sich dabei ganz normal über localhost mit dem in Docker laufenden Postgres/Redis (Ports sind in compose.yaml nach außen freigegeben).

LLM-Aufgaben (Extraktion, Interview, Outline, Widersprüche, Writer, Embeddings): LM Studio spricht OpenAI's /chat/completions+/embeddings-API, nicht Ollamas natives /api/chat — deshalb ist es als eigener Provider openai_compatible verdrahtet (ai/providers/openai_compatible.py), nicht einfach über BOOKCLAW_OLLAMA_BASE_URL erreichbar. Einrichtung:

  1. LM Studio starten, den gewünschten Server-Port merken (Standard: 1234) und mindestens ein Chat-Modell sowie optional ein Embedding-Modell laden.
  2. compose.yaml setzt für api/worker bereits extra_hosts: host.docker.internal:host-gateway — Container erreichen den Host also über den Hostnamen host.docker.internal, nicht localhost.
  3. BOOKCLAW_OPENAI_COMPATIBLE_BASE_URL zeigt standardmäßig schon auf http://host.docker.internal:1234/v1 (compose.yaml); anpassen, falls LM Studio einen anderen Port nutzt. BOOKCLAW_OPENAI_COMPATIBLE_API_KEY nur setzen, falls der Server das verlangt (LM Studio normalerweise nicht).
  4. Pro Projekt in den Einstellungen ("KI-Modelle & Provider") die gewünschte(n) Aufgabe(n) auf Provider openai_compatible umstellen und den exakten Modellnamen aus LM Studio eintragen (siehe GET http://localhost:1234/v1/models für die genaue Schreibweise).

Für Variante B (Worker außerhalb von Docker) direkt localhost statt host.docker.internal verwenden — dort läuft ohnehin kein Docker-Networking dazwischen.

Qualitätssicherung

ruff check .
ruff format --check .
mypy src
pytest

Architekturprinzipien (Kurzfassung)

  1. Sources sind unveränderlich (Source Immutability).
  2. KI-Ausgaben sind keine Wahrheit – Confirmation ist eine Nutzerentscheidung.
  3. Provenance ist Pflicht: jeder Claim führt auf seine Quelle zurück.
  4. Unsicherheit ist Daten, keine erfundene Präzision.
  5. Domain-Logik ist providerunabhängig (kein Vendor-Lock-in).
  6. Kein automatischer Fallback von lokal nach Cloud.
  7. Menschliche Entscheidungen überschreiben KI-Entscheidungen.
  8. Offener Export ist Kernfeature, kein Nachgedanke.

Details siehe docs/DEVELOPER_GUIDELINES.md.

Lizenz

Siehe LICENSE.