# 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. Offline-Bibliothek / "nur herunterladen"-Modus Oben in der GUI laesst sich per Umschalter zwischen drei Modi waehlen: - **"Auf USB-Stick schreiben"** (Standard) — wie bisher, eine Version, ein kompletter Stick. - **"Multiboot-Stick (mehrere Versionen)"** — siehe Abschnitt 4 unten. - **"Nur herunterladen (Offline-Bibliothek)"** — laedt ausschliesslich das Original-Image (`BaseSystem.dmg`/`InstallESD.dmg` bzw. `InstallAssistant.pkg`, je nach Version) und legt es **dauerhaft** unter `./downloads/library//` ab (inkl. `meta.json` mit Titel/Version/Groesse/Download-Zeitpunkt). Kein Geraet noetig, nichts wird geloescht. Damit kannst du dir vorab — solange Internet da ist — eine komplette Offline-Sammlung aller verfuegbaren macOS-Versionen aufbauen. Weil die Downloads im Bind-Mount `./downloads` liegen (siehe oben, kein Docker-Volume), reicht es, den kompletten Projektordner (inkl. `downloads/library/`) auf eine externe Platte zu kopieren — dort ist dann alles fuer den Stick-Bau parat, auch ganz ohne Internetzugang. **Wichtig:** ein "Stick erstellen"-Job prueft VOR jedem Download automatisch, ob das Image schon in der Bibliothek liegt (`installer.get_cached_payload`), und ueberspringt den Download dann komplett — das gilt fuer beide Modi. Jeder normale Stick-Bau traegt also nebenbei zur Bibliothek bei, und ein spaeterer Stick-Bau aus derselben Bibliothek (z.B. von der externen Platte) laedt nichts erneut herunter. Die GUI zeigt oben die Gesamtgroesse + Anzahl bereits heruntergeladener Installer an, und markiert bereits vorhandene Versionen in der Auswahlliste mit "bereits offline vorhanden". ### 4. Multiboot-Stick (mehrere macOS-Versionen auf einem Stick) Im Modus **"Multiboot-Stick"** waehlst du in Schritt 1 statt einer Version per Checkbox **2 bis 8** Versionen gleichzeitig aus. Das Tool baut daraus einen einzigen Stick mit **einer eigenen GPT-Partition pro Version**. **Warum das funktioniert:** Macs EFI-Bootmanager (beim Start `Alt`/`Option` gedrueckt halten) scannt beim Boot-Picker **alle** Partitionen eines angeschlossenen Datentraegers nach bootfaehigen Volumes, nicht nur die erste. Legt man also mehrere `Apple_HFS`-Partitionen an und schreibt in jede das vollstaendige, fuer sich bootfaehige Installer-Image, zeigt der Boot-Picker automatisch alle als eigene Wahlmoeglichkeiten an. Das ist eine in der Mac-Community seit Jahren dokumentierte Technik, kein Neuland dieses Tools. **Ablauf im Backend (`backend/installer.py`, `_run_multiboot_job`):** 1. Alle gewaehlten Images werden (aus der Bibliothek oder frisch) geladen und wie im Einzel-Stick-Modus konvertiert -- ERST wenn alle fertig konvertiert sind, geht's weiter (kein Halb-Zustand auf dem Stick). 2. Anhand der tatsaechlichen Image-Groessen + Puffer (`PARTITION_PADDING_MB`) wird geprueft, ob der ausgewaehlte Stick ueberhaupt gross genug ist -- **bevor** irgendetwas geschrieben wird. Ist er zu klein, bricht der Job mit einer klaren Fehlermeldung (benoetigt vs. vorhanden in GB) ab, ohne den Stick anzuruehren. 3. `wipefs -a` + `sgdisk --zap-all` loeschen den Stick komplett, dann legt `sgdisk` in einem Rutsch N Partitionen an (Typ `AF00` = Apple HFS/HFS+, mit Label = Titel/Version der jeweiligen macOS-Version, sichtbar im Boot-Picker). 4. Jedes Image wird per `dd` in seine eigene Partition geschrieben (`/dev/sdb1`, `/dev/sdb2`, ...), nicht auf das gesamte Geraet. **Ehrliche Einschraenkung -- als experimentell markiert:** - Fuer den `legacy_dmg`-Pfad (bis Catalina) ist die Technik gut etabliert und robust. - Fuer den `recovery`-Pfad (Big Sur+) erbt Multiboot dieselbe Unsicherheit wie der normale Einzel-Stick-Modus (siehe Abschnitt 2) -- kommt on top zur Multiboot-Partitionierung selbst dazu. Mischungen aus alten + neuen Versionen auf einem Stick sind technisch kein Problem, aber jede Big-Sur+-Version im Mix bleibt der fragilste Teil. - Groessenbedarf: 3 Versionen macOS koennen leicht 30-50GB+ zusammen ergeben, ein Stick mit 64GB+ ist fuer sinnvolle Kombinationen realistisch. - Es gab noch **keinen echten Boot-Test auf echter Mac-Hardware** durch dieses Projekt selbst -- die Partitionierungs-/Schreib-Logik ist gegen einen echten Docker-Host verifiziert (Build + Job-Ablauf), das tatsaechliche "zeigt der Boot-Picker alle Partitionen an und bootet jede" muss beim ersten echten Einsatz am Mac verifiziert werden. ### 5. 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) ## Alternative: nativer Mac-Weg (createinstallmedia) Wenn zufaellig ein echter Mac zur Verfuegung steht (macOS 10.15+), gibt es unter [`native-macos/`](native-macos/) ein eigenstaendiges Terminal-Script (`create-stick.sh`), das statt des Recovery-Extraktions-Workarounds die **echte** `createinstallmedia` nutzt — ueber Apples eigenen `softwareupdate --fetch-full-installer`-Befehl. Kein Docker noetig, keine Abhaengigkeiten ausser macOS-Bordmitteln. Details siehe dortiges README; wichtig: die beiden Wege sind bewusst unabhaengig, weil `createinstallmedia` ein anderes Installer-Format braucht als die rohen Katalog-Images, die das Docker-Tool hier im Root verwendet. ## 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. 4. Der Multiboot-Modus (Abschnitt 4) wurde bislang nur end-to-end bis zur fertig geschriebenen Partitionstabelle/den Images verifiziert, NICHT durch einen echten Boot-Test auf Mac-Hardware. Die zugrundeliegende Multi-Partition-Bootpicker-Technik ist etabliertes Community-Wissen, aber "funktioniert bei diesem Tool nachweislich" != "wurde von uns am Mac gebootet".