# macOS USB Installer Tool Lokale Web-GUI (Docker Compose) die macOS-Installer direkt von Apples Servern laedt und einen bootfaehigen USB-Stick daraus baut — ohne Mac. ## Start ```bash docker compose up --build ``` Dann im Browser: **http://127.0.0.1:8080** Der Port bindet bewusst NUR an `127.0.0.1` (nicht `0.0.0.0`) — von aussen im Netzwerk nicht erreichbar. ## Wie es funktioniert ### 1. Versions-Liste (automatisch aktuell) Beim Container-Start wird gegen Apples echten Software-Update-Katalog (`swscan.apple.com`) geprobt, welche macOS-Installer aktuell verfuegbar sind. Das Namensschema der Katalog-URLs ist bekannt und wurde live verifiziert (Stand: Erstellung dieses Tools). Wenn Apple eine neue macOS-Hauptversion veroeffentlicht, taucht sie **automatisch beim naechsten Container-Start** in der Liste auf — ohne Code-Aenderung, solange Apple das Namensschema beibehaelt (das ist seit vielen Jahren stabil, aber keine 100%-Garantie fuer alle Zeit). Per "neu laden"-Button in der GUI kann der Katalog auch ohne Neustart aktualisiert werden. ### 2. Zwei Installations-Pfade | macOS-Version | Pfad | Status | |---|---|---| | bis Catalina (10.15) | `legacy_dmg`: BaseSystem.dmg direkt per `dd` auf den Stick | **robust, gut etabliert** | | ab Big Sur (11) aufwaerts | `recovery`: `InstallAssistant.pkg` wird entpackt (`xar` → `pbzx` → `cpio`), das darin enthaltene `SharedSupport.dmg`/`BaseSystem.dmg` wird dann per `dd` geschrieben | **experimentell** | **Warum der Unterschied:** Ab macOS Big Sur liefert Apple keinen fertigen Installer-Datentraeger mehr, sondern ein Programm-Paket. Das eigentliche Tool das daraus einen vollstaendigen Multi-Volume-Installer-Stick baut (`createinstallmedia`) laeuft nur unter echtem macOS. Dieses Tool geht stattdessen den Community-erprobten Weg: das darin enthaltene Recovery-/BaseSystem-Image direkt extrahieren und schreiben. Der Stick bootet damit in ein Internet-Recovery-System, das den Rest der Installation von Apples Servern nachlaedt. **Das ist der fragilste Teil des ganzen Tools.** Apple hat den internen Aufbau von `InstallAssistant.pkg` in der Vergangenheit leicht veraendert. Wenn die Extraktion fehlschlaegt (Fehlermeldung im Log-Fenster), ist das kein Bug im eigentlichen Sinn, sondern ein Zeichen dass sich intern was geaendert hat — dann muss `backend/installer.py` (`_extract_recovery_dmg`) angepasst werden. ### 3. USB-Geraete-Erkennung Der Container laeuft mit `privileged: true` und mountet `/dev` sowie `/run/udev` vom Host durch — noetig, um Sticks direkt zu partitionieren und zu beschreiben. Das ist bewusst so gebaut, bedeutet aber: der Container hat vollen Zugriff auf **alle** Block-Devices des Rechners. Deshalb: - Port ist NUR auf `127.0.0.1` gebunden (kein Netzwerk-Zugriff von aussen). - Die System-Platte (auf der `/` liegt) wird automatisch als Ziel gesperrt. - Nur Geraete mit USB-Transport werden als Ziel akzeptiert. - Vor dem Schreiben muss der exakte Geraetepfad (z.B. `/dev/sdb`) nochmal von Hand eingetippt werden — kein versehentliches Ein-Klick-Loeschen. **Trotzdem gilt:** das Tool schreibt mit `dd` und `wipefs` direkt auf ein Block-Device. Wenn du das falsche Geraet auswaehlst, sind die Daten weg. Vor dem Start immer pruefen, welcher Stick tatsaechlich gemeint ist (Groesse und Modell in der GUI vergleichen). ## Build-Hinweis (hfsprogs / xar / pbzx) Alles unten wurde am 2026-07-18 live gegen einen echten `docker compose build` + `up` auf einem echten Docker-Host getestet (nicht nur behauptet): - `hfsprogs` liegt in Debians non-free-Bereich. WICHTIG: `bookworm-slim` nutzt das neue **deb822**-Sourcen-Format (`/etc/apt/sources.list.d/ debian.sources` statt der alten `sources.list`). Eine zusaetzliche separate `sources.list`-Datei fuer dieselbe `deb.debian.org`-URI OHNE `Signed-By` fuehrt zu `Conflicting values set for option Signed-By` und bricht `apt-get update` komplett ab. Der Dockerfile patcht deshalb die bestehende `debian.sources` per `sed` (Components-Zeile um `contrib non-free non-free-firmware` erweitert) statt eine zweite Quelle anzulegen. - `xar` gibt es in `bookworm` ueberhaupt nicht als Paket, wird aus dem Upstream-Repo `mackyle/xar` gebaut. Dessen `configure.ac` prueft OpenSSL ueber `AC_CHECK_LIB([crypto], [OpenSSL_add_all_ciphers])` -- diese Funktion ist in OpenSSL 3.x (Debian bookworm) nur noch ein no-op-Praeprozessor-Makro in `evp.h`, kein linkbares Symbol mehr. Der Autoconf-Test schlaegt deshalb fehl ("Cannot build without libcrypto"), obwohl `libssl-dev` korrekt installiert ist. Fix: der Dockerfile biegt den Probe-Funktionsnamen per `sed` auf `EVP_EncryptInit` um (gegen `libcrypto.so.3` per `nm` verifiziert, seit jeher stabil exportiert). - `xar` MUSS vor `pbzx` gebaut werden -- `pbzx` linkt gegen `-lxar` und `xar/xar.h`. - `pbzx`: das Repo `mackyle/pbzx` existiert nicht (mehr) auf GitHub (404). Git meldet das irrefuehrend als `could not read Username for 'https://github.com'`, weil GitHub bei `git-upload-pack`-Requests gegen nichtexistente/private Repos aus Anti-Enumeration-Gruenden 401 statt 404 zurueckgibt. Fix: Community-Standard-Fork [`NiklasRosenstein/pbzx`](https://github.com/NiklasRosenstein/pbzx) (u.a. in OSX-KVM genutzt) wird stattdessen geklont. Macht den ersten Build minimal langsamer, aber zuverlaessiger als ein Distro-Wechsel. ## Katalog-Bugs (gefunden + gefixt, 2026-07-18) Zwei echte Bugs in `backend/catalog.py`, beide live gegen den echten Apple-Katalog (`swscan.apple.com`) reproduziert und verifiziert: 1. **Doppeltes Entpacken:** Apples Server schickt den Katalog mit `Content-Encoding: x-gzip`. `requests`/`urllib3` dekomprimiert das bereits TRANSPARENT -- der Code rief zusaetzlich `gzip.decompress()` drauf auf, was mit `BadGzipFile: Not a gzipped file` crashte (die Gzip-Magic-Bytes waren durch die automatische Dekompression laengst weg). Fix: nur noch entpacken wenn die Bytes wirklich noch mit der Gzip-Magic (`\x1f\x8b`) anfangen. 2. **Big Sur+ wurde komplett ausgefiltert:** `_is_macos_installer()` prüfte nur auf das klassische Schema (`ExtendedMetaInfo.InstallAssistantPackageIdentifiers.OSInstall == "com.apple.mpkg.OSInstall"`, gueltig bis Catalina). Ab Big Sur nutzt Apple stattdessen einen `SharedSupport`-Key (`com.apple.pkg.InstallAssistant.macOSBigSur`, `...macOSTahoe`, etc.) -- ohne den alten Key. Der Filter hat dadurch STILLSCHWEIGEND alle Big-Sur-bis-Tahoe-Installer verworfen, obwohl sie im selben Katalog laengst enthalten waren (live verifiziert: 24 InstallAssistant.pkg- Eintraege im Katalog, 0 davon trafen den alten Filter). Genau der `recovery`-Pfad, den dieses Tool eigentlich anbieten soll. Fix: `_is_macos_installer` erkennt jetzt beide Schemata; `_fetch_product_ metadata` faellt bei fehlendem `ServerMetadataURL` (Big Sur+ hat keins) auf den Namen aus dem `SharedSupport`-Identifier zurueck. Nach dem Fix: 37 statt 13 Installer gefunden, inkl. macOS Tahoe. Beide Fixes wurden nicht nur im Code geaendert, sondern gegen den echten, frisch gebauten Container end-to-end verifiziert (`/api/versions` liefert jetzt tatsaechlich Big Sur bis Tahoe zurueck). ## Voraussetzungen auf dem Host - Docker + Docker Compose - Linux (getestet gegen die Grundannahmen von `lsblk`/`/dev`/`udev`; auf macOS/Windows-Docker-Desktop funktioniert das Device-Passthrough so nicht) ## Bekannte Grenzen (ehrlich, nicht schoengeredet) 1. Der `recovery`-Pfad (Big Sur+) ist nicht production-hardened getestet worden, sondern nach dokumentiertem, in der Hackintosh-/Recovery-Community etabliertem Verfahren gebaut. Rechne bei sehr neuen macOS-Versionen mit Nacharbeit. 2. Die Katalog-Zukunftssicherheit haengt an Apples URL-Namensschema. Bricht Apple das Schema, muss `backend/catalog.py` angepasst werden (kurzer Fix, kein Rewrite). 3. Kein Windows/macOS-Docker-Host-Support fuer das Device-Passthrough — nur natives Linux.