How the Jailbreak First-Boot Finalization Process Works in vPhone-CLI

The jailbreak first-boot finalization process runs automatically via a LaunchDaemon that executes /cores/vphone_jb_setup.sh on the initial normal boot, performing idempotent system configuration, package installation, and environment setup before writing a completion marker.

The jailbreak first-boot finalization process in the Lakr233/vphone-cli repository ensures that iOS virtual machines transition from a raw Procursus bootstrap to a fully functional jailbroken state. This process is orchestrated by a Bash script deployed to /cores/vphone_jb_setup.sh and triggered by a LaunchDaemon defined in vphone_jb_setup.plist. The script implements defensive programming practices—checking for completion markers and handling errors through set -uo pipefail—to ensure safe re-execution while establishing SSH access, installing Sileo, and configuring the package manager ecosystem.

How the LaunchDaemon Triggers the Finalization

When you install a jailbreak (.jb) or experimental (.exp) variant through vPhone-CLI, the tool copies vphone_jb_setup.sh into the VM’s /cores directory and installs vphone_jb_setup.plist into /Library/LaunchDaemons. This property list registers the script to execute automatically on the first normal boot of the iOS system.

The orchestrator code in VPhoneCreateOrchestrator.swift confirms this behavior at lines 39-44, outputting:

print("[*] JB finalization will run automatically on first normal boot")
print("    via /cores/vphone_jb_setup.sh (LaunchDaemon).")
print("    Monitor progress via vphoned file browser: /var/log/vphone_jb_setup.log")

All script output redirects to /var/log/vphone_jb_setup.log, enabling debugging through the vphoned vsock-based file browser.

Step-by-Step Breakdown of /cores/vphone_jb_setup.sh

The finalization script operates idempotently, checking for a done marker at /var/mobile/.vphone_jb_setup_done before proceeding (lines 38-42). If found, the script exits immediately to prevent duplicate configuration.

Environment Preparation and Idempotency

Before modifying system state, the script establishes error handling via set -uo pipefail and constructs a sanitized PATH variable. The initial PATH construction (lines 19-25) iterates through standard system locations and Procursus-specific directories:

for d in \
    /var/jb/usr/bin \
    /var/jb/usr/sbin \
    /usr/local/bin \
    /usr/bin \
    /bin \
    /usr/sbin \
    /sbin; do
    [ -d "$d" ] && PATH="$PATH:$d"
done
export PATH

This ensures all subsequent commands resolve correctly regardless of the host environment.

Bootstrap Execution and PATH Refresh

The script executes /var/jb/prep_bootstrap.sh if present (lines 27-33), which finalizes the Procursus bootstrap and removes itself upon completion. After bootstrap execution, the script rebuilds the PATH (lines 35-44) to include any newly installed binaries from /var/jb.

System-Level Modifications

Launchctl Replacement: To avoid crashes from missing symbols in the bundled version, the script replaces Procursus’s launchctl with the reliable iosbinpack64 variant at lines 69-77:

ln -sf "$IOSBINPACK_LAUNCHCTL" "$JB_TARGET/usr/bin/launchctl"

Symlink Creation: The script creates a critical symlink at lines 84-90, linking /private/var/jb to the Procursus target directory. This establishes the standard jailbreak root path expected by many tweak packages.

Ownership and Permission Fixes: Lines 94-101 correct filesystem metadata by setting ownership to 501:501 for mobile user directories and 0:0 for system directories, while applying 0755 permissions to library paths.

SSH and Security Setup

The script generates Dropbear host keys at lines 107-119 using dropbearkey, creating both RSA and ECDSA keys under /var/dropbear with strict 0600 permissions:

"$DROPBEARKEY" -t rsa -f /var/dropbear/dropbear_rsa_host_key
"$DROPBEARKEY" -t ecdsa -f /var/dropbear/dropbear_ecdsa_host_key
chmod 0600 /var/dropbear/*

Package Manager Installation and Repository Configuration

Sileo Installation: Lines 61-69 install the Sileo package manager from a pre-staged .deb file located in the boot hash directory:

dpkg -i "$SILEO_DEB_PATH"

Extra Package Installation: The script scans the debs/ folder (lines 77-96), comparing installed versions against available packages and installing any missing or newer dependencies.

APT Repository Setup: The script configures the Havoc and Frida repositories (lines 18-52), updates the package index with apt-get update, installs the libkrw0-tfp0 kernel read/write library, and performs a full system upgrade:

printf '%s\n' 'deb https://havoc.app/ ./' > "$HAVOC_LIST"
apt-get update
apt-get install -y libkrw0-tfp0
apt-get upgrade -y

TrollStore Lite and App Registration

The script installs TrollStore Lite at lines 60-73 using apt-get install -y com.opa334.trollstorelite. Success sets TROLLSTORE_READY=1, which gates the final completion marker.

For iOS 27 compatibility, the standard uicache -a command is non-functional. Instead, the script executes /cores/vpregister (lines 88-92) to register applications through the containerized LaunchServices API:

if [ -x /cores/vpregister ]; then
    /cores/vpregister
fi

Shell Profile Configuration

Lines 98-104 create .bashrc and .bash_profile for the root user that source /var/jb/etc/profile, ensuring the jailbreak PATH persists across SSH sessions:

printf '%s\n' 'source /var/jb/etc/profile' > "$profile"

Finalization and Marker Creation

The script concludes at lines 108-113 by writing the completion marker only if TrollStore Lite installed successfully:

if [ "$TROLLSTORE_READY" = "1" ]; then
    : > "$DONE_MARKER"
    echo "[+] vphone_jb_setup.sh completed successfully"
else
    echo "[!] Core steps done, but TrollStore not ready; marker not set"
fi

Monitoring and Debugging the Process

You can monitor the first-boot finalization in real-time by accessing the VM’s log file:


# Via vphoned file browser or SSH

tail -f /var/log/vphone_jb_setup.log

To verify whether finalization has completed:

if [ -f /var/mobile/.vphone_jb_setup_done ]; then
    echo "Jailbreak finalization complete"
else
    echo "Finalization pending or failed"
fi

If you need to re-trigger the script manually for debugging:

launchctl load /Library/LaunchDaemons/com.vphone.jbsetup.plist
launchctl start com.vphone.jbsetup

Summary

  • The jailbreak first-boot finalization process runs via a LaunchDaemon executing /cores/vphone_jb_setup.sh automatically on the initial boot of jailbroken VMs created by vPhone-CLI.
  • The script is idempotent, checking for /var/mobile/.vphone_jb_setup_done before processing and redirecting all output to /var/log/vphone_jb_setup.log.
  • Key operations include replacing launchctl, symlinking /var/jb, fixing filesystem permissions, generating Dropbear SSH keys, running prep_bootstrap.sh, and installing Sileo.
  • The script configures APT repositories (Havoc, Frida), installs TrollStore Lite, and handles iOS 27-specific app registration through /cores/vpregister.
  • Completion depends on successful TrollStore installation, ensuring the system only marks itself as finalized when fully operational.

Frequently Asked Questions

What happens if the jailbreak first-boot finalization script fails halfway through?

If vphone_jb_setup.sh encounters an error, the set -uo pipefail directive causes immediate termination with an error code. Because the script writes the completion marker only after successful TrollStore installation, you can safely reboot the VM to trigger the LaunchDaemon again—the script will resume from the beginning or skip already-completed steps based on filesystem state.

Can I run the finalization script manually without the LaunchDaemon?

Yes. You can execute /cores/vphone_jb_setup.sh directly from an SSH session or terminal as root. However, ensure you set the environment variables the script expects (such as BOOT_HASH pointing to the pre-boot directory) or load the LaunchDaemon via launchctl load /Library/LaunchDaemons/com.vphone.jbsetup.plist followed by launchctl start com.vphone.jbsetup to maintain proper execution context.

Where does the script install Sileo and how does it handle custom packages?

The script installs Sileo from a .deb file located at /cores/${BOOT_HASH}/sileo.deb using dpkg -i (lines 61-69). For custom packages, it scans the /cores/${BOOT_HASH}/debs/ directory and installs any packages not already present or newer than installed versions (lines 77-96), making it extensible for additional tools beyond the base jailbreak.

Why does the script replace the Procursus launchctl binary?

The bundled Procursus launchctl may crash on certain iOS versions due to missing symbols or library mismatches. The script replaces it with a known-working version from iosbinpack64 (lines 69-77), preserving the original as launchctl.procursus. This ensures reliable daemon management during the bootstrapping phase without breaking compatibility with Procursus utilities.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →