Cross-compiled on a Mac, installed with apt
Most packages are built on a Mac in Docker against the Procursus toolchain, patched, signed for the capabilities they need, and shipped as Debian packages to a Sileo repo.
Standing on Procursus
The rootless bootstrap on these devices is Procursus, which is itself a macOS-hosted cross-compile system that already ships a full X11 stack prebuilt for iphoneos-arm64. The approach is to stand on that rather than rebuild it: use the toolchain, and spend the effort on the parts Procursus does not provide, starting with the native display server.
Most upstream changes are kept as quilt-style patch series next to the port that needs them, applied by the recipe, so a rebuild reproduces them. It is not universal: the KDE and Qt tracks are still moving fast enough that some of their changes live in build scripts instead of in a series, and converting those is on hold until that work settles.
The pipeline runs in Docker, but "runs in Docker" is not the same as one button. There is no top-level orchestrator: each build driver is invoked by hand with the docker run spelled out in its header, several assume a warm named volume left by an earlier driver, and a few stages are not in Docker at all. ANGLE is built on the Mac side, and the GNOME introspection typelibs are generated on the iPad, which makes the device a build stage rather than just a test target.
The harder cross-compiles
- mozjs 115
- SpiderMonkey cross-compiled from Linux to iOS, with its JIT running on the A10. This is what lets gjs, and therefore GNOME Shell, run.
- bun
- Bun, the JavaScript runtime, cross-compiled to iOS. Its JIT and
bun:ffirun even though iOS refuses an executable heap, by relocating generated code throughmmap. opencode runs a full agent turn on the device. - Ladybird
- An independent browser engine: C++23 against the iOS SDK's libc++, Rust crates cross-built, Skia through GN, and a private vendored copy of OpenSSL so the browser's newer TLS cannot shadow the one apt and sshd depend on.
- Introspection
- The GNOME Shell boot typelibs were generated on the device with the native Procursus toolchain, then packaged as
xios-gnome-typelibs. - ICU
- Built native-then-cross so its data compiler runs on the host. Two versions ship: 74 for the desktop stack, and 78 for Ladybird, which pins an exact match.
- ANGLE
- Google's ANGLE built from source with the Metal backend, patched to admit the A10 to ES3, and packaged as the
angledeb. See the GPU path. - Qt6 and KF6
- Qt 6.6 with eight modules on top of qtbase, a KF6 subset of about fifty runtime packages, KWin, Plasma Desktop, Nano and Mobile, and a growing set of KDE apps: Konsole, Kate, Dolphin, KCalc, Ark, Gwenview, KWrite and Okular.
Signing is a build step
A cross-compiled binary is not runnable on iOS until it is fakesigned with ldid, and what it is allowed to do depends on the entitlements attached at that moment. Talking to the GPU, importing an IOSurface from another process and inspecting other processes are all separate capabilities, so packages are re-signed at publish time by classifying each Mach-O into a profile instead of giving every binary the same set.
There is a sharp edge here: the Homebrew build of ldid emits DER entitlements and the one inside the container does not, and modern iOS wants DER for exactly the IOKit and IOSurface capabilities this stack needs. The publish step refuses a binary whose entitlement slot is missing, so nothing ships that would have failed at launch.
Separately, every deb gets a MinimumOSVersion stamped from its real dependency closure, which is why the flavors advertise different iOS floors. That pass has to run last, after the package set stops changing.
The repo
Packages are published to a self-contained apt repo, the same kind every jailbreak uses: a signed index, generated depictions, and the payloads themselves offloaded to blob storage. There are two of them. A staging repo takes everything first; production is promoted by hand, so the published set can sit behind the tree.
The gate between them exists because of a specific accident. A package that shadowed the bootstrap's OpenSSL with a newer one took out sshd and apt on the device at once, which is a bad day when the device is also a build stage. Publishing now refuses any deb that replaces a Procursus package unless it is a genuine drop-in superset, with per-package waivers that have to be written down. The browser's newer TLS ended up vendored into a private directory for exactly this reason.
There is no single xios package. You install one of xios-gnome, xios-kde, xios-native or xios-x11, and each pulls in the shared xios-runtime base — plus xios-core for the three fullscreen flavors. Sileo is the flavor chooser; nothing custom ships to do that job.
One prefix, for now
Everything is built for the rootless layout and bakes /var/jb into paths, launchers and entitlements. A rootful target is designed and the first pieces are in the tree: build targets come from a descriptor file instead of a hardcoded prefix, and a handful of packages render their payload from templates. Nothing rootful is built or published yet, and the two binary packages that were converted refuse to produce a rootful deb until a rootful server has been built and smoke-tested on hardware.