Docker
Die ganze Toolchain ohne lokale Node-Installation ausführen — einschließlich Studio, und ein Image für jede Umgebung.
Das Image ist Node 22 (bewusst nicht 24 — siehe das Dockerfile) plus die gebauten Pakete.
pnpm run docker:build # docker build -t handbook:local .Befehle ausführen
HANDBOOK_SOURCE=/src und HANDBOOK_WORK=/work sind ins Image eingebacken, Sie mounten
also nur Volumes — kein --source oder --work nötig:
# free, no key
docker run --rm -v "$PWD:/src:ro" -v handbook-work:/work handbook:local analyze
# with an endpoint
docker run --rm --env-file .env \
-v "$PWD:/src:ro" -v handbook-work:/work \
handbook:local generate --detail deep
# render, then get the output back out
docker run --rm -v "$PWD:/src:ro" -v handbook-work:/work \
handbook:local render --html --agent-site --llms-txt
docker run --rm -v handbook-work:/work -v "$PWD/out:/out" \
--entrypoint cp handbook:local -R /work/handbook /out/Die Quelle schreibgeschützt zu mounten (:ro) ist eine gute Gewohnheit für alles
außer apply.
Umgebungsvariablen
Dockers eigenes --env-file legt sich über das .env-Laden der Toolchain — beide
greifen, und eine so übergebene OPENAI_*-Variable ist genauso sichtbar, wie es ein
Shell-Export wäre.
docker run --rm --env-file .env -v "$PWD:/src:ro" -v handbook-work:/work \
handbook:local generateEin Image, jede Umgebung
.env*-Dateien werden nie ins Image eingebacken — siehe .dockerignore. Wählen Sie
eine Umgebung zur Laufzeit:
docker run --rm --env-file .env.prod -e HANDBOOK_ENV=prod \
-v "$PWD:/src:ro" -v handbook-work:/work \
handbook:local generateOder mounten Sie eine Konfigurationsdatei:
docker run --rm \
-v "$PWD:/src:ro" -v handbook-work:/work \
-v "$PWD/handbook.config.prod.yaml:/cfg.yaml:ro" \
handbook:local --config /cfg.yaml generateStudio
pnpm run docker:studio # docker compose up --build studioÖffnen Sie dann http://localhost:4860.
Nur localhost funktioniert — keine LAN-IP, nicht der Containername
Studios CSRF-Abwehr prüft den Host-Request-Header, nicht den Socket. Ein Container
muss 0.0.0.0 binden, damit der veröffentlichte Port überhaupt erreichbar ist
(HANDBOOK_STUDIO_HOST=0.0.0.0 in der Compose-Datei), aber das erweitert nicht, wer
mit ihm sprechen darf. Ein Browserzugriff vom Host sendet weiterhin
Host: localhost:4860 und passiert; eine Anfrage, die eine LAN-IP oder den Hostnamen
des studio-Containers nennt, wird absichtlich mit 403 abgewiesen.
Fernzugriff ist ein bewusst nicht implementiertes, separates Feature — es bräuchte eine explizite Positivliste — und kein Fehler in dieser Abwehr.
services:
studio:
build: .
command: studio
environment:
HANDBOOK_STUDIO_HOST: 0.0.0.0
ports:
- '127.0.0.1:4860:4860'
volumes:
- ./:/src:ro
- handbook-work:/workDen Port als 127.0.0.1:4860:4860 statt 4860:4860 zu veröffentlichen hält ihn
zusätzlich von Ihrer LAN-Schnittstelle fern — doppelte Absicherung zusätzlich zur
Host-Header-Prüfung.
Volumes
| Pfad | Inhalt | Empfehlung |
|---|---|---|
/src | Ihr Quellbaum | Für alles außer apply mit :ro mounten |
/work | Handbooks-Artefakte | Ein benanntes Volume, damit es zwischen Läufen überlebt |
In CI
jobs:
handbook:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t handbook:ci .
- run: |
docker run --rm \
-e OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }} \
-v "$PWD:/src:ro" -v "$PWD/work:/work" \
handbook:ci generate --detail brief
- run: |
docker run --rm -v "$PWD:/src:ro" -v "$PWD/work:/work" \
handbook:ci render --html --agent-site --llms-txt
- uses: actions/upload-artifact@v4
with: { name: handbook, path: work/handbook }analyze, render, skill und validate brauchen gar keinen Schlüssel, ein
Fork-sicherer Workflow kann sie also bei jedem Pull Request ausführen und generate für
main reservieren.
Warum Node 22 und nicht 24
Eine der gebündelten Tree-sitter-Grammatiken (Swift) bricht den Prozess auf V8 ≥ 13 ab.
Node 22 liegt unterhalb dieser Grenze, das Image braucht also keine besonderen Flags. Auf
Node 24 verweigert der Adapter bei der Erkennung und fordert Sie auf, --liftoff-only zu
übergeben; das Image auf 22 zu pinnen erspart die Frage ganz. Siehe
Sprachunterstützung.