Skip to content

Skip Setup ​

A fresh Data seed starts the Setup app on first boot. The payload can mark Setup as done before the image is written, so the device boots to SpringBoard.

Command ​

sh
./unixnu build payload \
  --data-ramdisk data.dbr \
  --skip-setup \
  --trust-cache trustcache.img \
  --unique-chip-id <hex>

--skip-setup requires --data-ramdisk. The payload step reads [payload] skip_setup_tool from the board file and runs it on a copy of the seed.

toml
[payload]
skip_setup_tool = "scripts/seed-data-skip-setup.py"

The original seed stays unchanged. The payload uses the edited copy as Payloads/scratch-data.img.

What the tool changes ​

scripts/seed-data-skip-setup.py expands the packed APFS Data seed, attaches it read-write, merges preferences into /private/var/mobile/Library/Preferences, and packs the result again.

Preference fileValues
com.apple.purplebuddySetupDone = true, SetupFinishedAllSteps = true, SetupVersion = 11, daemonizeNeedsToRun = false
com.apple.purplebuddy.notbackedupEvery mini-buddy flag is false: diagnostics, HSA2, keychain sync, Mesa, payment, standard, silent ICDP
com.apple.managedconfiguration.notbackedupLockdownActivationIndicatesCloudConfigurationAvailable = false
com.apple.springboardBKNoWatchdogs = true, FBLaunchWatchdogExceptions = { com.apple.purplebuddy: 100 }, MCForceShowCloudConfigurationUI = false
com.apple.migrationLastSystemVersion = 21H461, DMUserDataDisposition = { SyntheticDidUpgrade = false, buildVersion = 21H461 }

The tool also creates these directories under /private/var/containers:

Bundle
Data/System
Shared/SystemGroup
Temp

The volume UUID stays the same. The APFS container magic is checked again after the edit.

Build version ​

The script sets:

python
BUILD, VERSION = '21H461', '17.7.11'

Update both values when the board targets a different iOS build. LastSystemVersion and DMUserDataDisposition.buildVersion must match the IPSW.

Device tree ​

The ADT adds four /chosen properties with --ipad-identity:

PropertyValue
certificate-production-status0
effective-production-status-ap0
certificate-security-mode1
effective-security-mode-sep1

These describe a development-fused, undemoted board. mobileactivationd reads them through MobileGestalt and takes its native activation path without an activation record.

Jobs ​

Setup depends on services that do not run on this platform. The board disables them with boot arguments:

sheng-disable-jobs=com.apple.identityservicesd,com.apple.locationd,com.apple.CommCenter,com.apple.iconservices.iconservicesagent

com.apple.CommCenter is the cellular service. The list is board specific. Keep the services the userland needs and remove the ones that block the first boot.

Output ​

The payload directory contains:

out/<board>/payload/data-skip-setup-<hash>.dbr
out/<board>/payload/payload.img
out/<board>/payload/payload.json

payload.json records:

FieldMeaning
data_ramdisk_sha256Hash of the edited seed
skip_setuptrue
payload_sha256Payload image hash

The edited seed is cached by the hash of the original. A different seed produces a new file.

Verify ​

  1. Build the payload with --skip-setup.
  2. Check payload.json has "skip_setup": true.
  3. Boot the image.
  4. The log reaches SpringBoard instead of the Setup pages.

The reference implementation is scripts/seed-data-skip-setup.py in this repository. The sheng port recorded the first hardware boot with these changes in commit 5eba9f5 of blu-sharky/sheng-ipados.

Next steps ​

Released under the BSD-2-Clause-Patent License.