Quick Answer: HackiOS userland is the user-space half of HackiOS, an independent research project by OOSEX GANG. It runs Apple's open-source XNU kernel with the unmodified iOS 16.5 user space on POCO X5 Pro 5G phones and in QEMU. The repository contains HackiOS's own init process, injected libraries, and image build scripts, but no Apple files. You fetch the iOS 16.5 IPSW yourself, and you start from the companion kernel repository.

If you landed on a repository called hackios-xnu-userland expecting a ready-to-flash iOS, you'll be surprised. The code here is the layer that sits above the kernel: the first process, the libraries that get injected into system daemons, and the scripts that assemble two APFS disk images. The HackiOS userland repository is deliberately thin on Apple material, and that design choice shapes everything else about how you use it.

Why You Start in the Kernel Repository, Not Here

Most people assume the userland repo is the entry point. It isn't. The README is blunt: the kernel, loader, UEFI code, and the shared common/ scripts live in hackios-xnu-kernel, and you should start there.

The two repositories are built as one tree. The kernel repo's common/setup.sh checks this repository out into the same directory, using a separate git directory named .userland.git. That means a plain git pull in the userland folder won't update the kernel side. Here's where people get tripped up: you have to use common/ugit to run git for this repository inside that tree.

This split is useful. Kernel changes and userland changes move at different speeds, and keeping them separate means a userland bug doesn't force you to rebuild the kernel.

What Is Actually in the Repository

The repo is organised around a handful of paths. Knowing what each one does saves a lot of confusion later.

PathWhat it doesWhy it matters
userland/build.shBuilds root.apfs and system.apfsEvery build option is documented at the top of the file
userland/src/Holds init.c (process 1), the injected libraries, the CoreAudio output device, and the OpenGL ES bridge to MesaThis is where HackiOS-specific behaviour lives
ios165/Contains fetch.sh, fetch-from-ipsw.py, and select-system.pyThese extract iOS 16.5 from Apple's IPSW on your own machine
tools/apfs/Contains mkapfs.py, a reader, checks, and write testsAPFS images are written on the computer, not on the phone

The injected libraries are the interesting part. inject.c targets backboardd, inject_sb.c targets SpringBoard, inject_media.c targets mediaserverd, and inject_tweaks.c covers tweaks. The eagl.c file bridges OpenGL ES calls to Mesa, which is how the window server gets a graphics path. The repo also includes a tiny libc called libmini for the static programs, so the native programs don't depend on the full iOS C library.

How the Images Are Built Without Apple's Files

The build flow is the part most people need to understand first. Run ios165/fetch.sh once. It downloads the iOS 16.5 IPSW from Apple's CDN to your machine and pulls out only the pieces needed. Then REST=1 userland/build.sh produces root.apfs and system.apfs.

Here's the sequence in practice:

  1. The fetch script downloads the IPSW and extracts iOS 16.5 locally.
  2. select-system.py decides which parts of iOS and its cryptexes go into system.apfs.
  3. build.sh compiles userland/src if you have the cross toolchain, or uses prebuilt/userland if you don't.
  4. mkapfs.py writes the two APFS images.

Adding GL=1 brings in OpenGL ES through Mesa for the window server, and the README says it's meant for use on the phone with gpu=1. The cross toolchain needs theos and the iPhoneOS16.5 SDK, which the kernel repo explains how to set up.

The repo's "no Apple files" rule is what makes this legally workable. It also means you need roughly the same disk space and time for the fetch step as any IPSW download, and you can't skip it.

The Counter-Intuitive Part: No Shell, No Problem

Here's the finding that surprised me most. A job list in HackiOS is not run by a shell. Instead, HackiOS's own init processes the RC_JOBS=file lines directly. Iöhe README is explicit: iOS 16.5's root has no shell, lines are split on blanks, there is no quoting, and there is no variable assignment. Only >> FILE redirection and a trailing & are understood.

Most people would expect a launch script to behave like a shell script. It doesn't, and that's the point. When a job line breaks, the cause is almost always the lack of quoting or expansion, not a bad path. Once you know that, debugging gets a lot faster. Write each job as a plain command with explicit arguments, and avoid any shell syntax you'd normally reach for.

The stock iOS launchd jobs have their own bookkeeping. mkjobs.py and jobinfo.py tell you which stock jobs are loaded and which process provides each Mach service. Reading that output before you change anything is the cheapest way to avoid breaking a service other jobs depend on.

Phone, QEMU, and the Graphics Question

The project runs in two places: a POCO X5 Pro 5G Android phone and QEMU. The repo doesn't go into detail about how each target is tuned, so treat any claim about performance on either as unverified until you test it yourself.

Graphics is the part with the most moving pieces. The repo ships Mesa 25.0.7 with HackiOS patches: freedreno support on Darwin and an AIR (Metal IR) frontend. The tools/mk-mesa.sh script fetches Mesa release 25.0.7, and tools/mesa/*.patch holds the changes. Shader checks live under tools/air/ and tools/metal/, with notes in docs/air.md.

Here's the honest caveat: the Metal driver work is described as work, not as finished. If you need a fully accelerated graphics stack, read the source and the patches before you commit to a build plan.

Licensing and What You Must Fetch Yourself

The licensing is split on purpose, and it affects how you build. The repository's own code is released under the MIT License. Mesa is also MIT-licensed, but it isn't in the repository. You fetch it with a script.

apfsprogs is GPL-2.0 and also not in the repo. The tools/apfs/check-image.sh script fetches and builds only apfsck. Procursus and Sileo, used for the package manager prefix via tools/mk-jb.sh, are likewise pulled in by scripts rather than shipped.

The README includes a clear disclaimer: HackiOS is an independent research project, not affiliated with, authorised, or endorsed by Apple Inc. Apple's copyright covers the iOS files, so you should treat any IPSW you download as Apple's property and comply with its terms.

Frequently Asked Questions

What is HackiOS userland?

HackiOS userland is the user-space half of the HackiOS research project. It contains HackiOS's own init process, injected libraries for backboardd, SpringBoard, and mediaserverd, the CoreAudio output device, and scripts that build the root and system APFS images. It runs on top of Apple's open-source XNU kernel, which lives in a separate companion repository.

How do I build HackiOS userland images?

Clone the kernel repository and run common/setup.sh so it brings in the userland repo. Then run ios165/fetch.sh once to extract iOS 16.5 from Apple's IPSW locally. Finally, run REST=1 userland/build.sh to produce root.apfs and system.apfs. Add GL=1 if you want OpenGL ES through Mesa.

Why don't HackiOS job lists work like shell scripts?

iOS 16.5's root has no shell, so HackiOS's init parses job lines itself. Lines are split on blanks, with no quoting and no variable assignment. Only >> FILE and a trailing & are understood. Write each job as a plain command with explicit arguments to avoid surprises.

Can I run HackiOS userland in QEMU instead of on a phone?

Yes. The project targets both a POCO X5 Pro 5G and QEMU, according to the README. The repo doesn't publish benchmarks for either target, so test the behaviour you care about on your own setup before relying on it.

Closing

The single most useful thing to take from this repository is the build boundary. Your userland images come from an IPSW you fetch yourself, and the job system is far stricter than a normal shell. Start in the kernel repository, read the header comments in userland/build.sh, and check what mkjobs.py reports about the stock launchd jobs. Try running common/ugit status after setup this week and note how the two repositories sit in one tree. Pass this to anyone who's trying to work out how the HackiOS userland gets built.