Verify the stable API and exact Capacitor iOS CORS contract before signing device-debug IPAs, with regression coverage and troubleshooting guidance.
18 KiB
Cable-Connected iPhone Debug IPA Runbook
This runbook covers development-signed iOS builds installed from an Ubuntu
workstation over USB. It is separate from the public App Store release path in
docs/app-store-release.md.
The device build is deliberately a second app:
- Debug bundle ID:
io.truckwash.app.debug - Debug display name:
Truck Wash Debug - Production bundle ID:
io.truckwash.app - Capacitor/Android app ID:
io.truckwash.twa - API:
https://api-v2.truckwash.io/master/api
Installing or uninstalling the debug app must not replace or remove the
production app. The debug workflow builds the Vue application in production
mode against the stable API; it does not use Vite's development /api default
or a live-reload server.
What Ubuntu Can And Cannot Do
The current Ubuntu workstation already has usbmuxd, the libimobiledevice
utilities, and ideviceinstaller. The current iPhone has previously been
trusted and paired. Run the repository doctor before every install because the
phone can still be locked, trust can be reset, or Developer Mode can be off.
This workflow supports:
- Inspecting pairing, activation, lock, Developer Mode, and install-service readiness.
- Installing and upgrading a valid development-signed IPA.
- Reading filtered device syslog and copying crash reports.
Apple does not provide Xcode, LLDB device debugging, or Safari Web Inspector on
Linux. CAPACITOR_DEBUG and get-task-allow make the IPA suitable for a
development device, but they do not create an official Linux LLDB or WebKit
debugger. Use a physical Mac with Xcode/Safari for breakpoints or Web Inspector.
Use TestFlight or a physical Mac if a new or beta iOS release is incompatible
with libimobiledevice; never weaken device security or signing validation as a
workaround.
One-Time iPhone Preparation
- Connect the iPhone directly with a data-capable USB cable.
- Unlock the phone and keep it awake. Tap Trust if iOS asks whether to trust this computer, then enter the device passcode.
- On iOS 16 or newer, open Settings -> Privacy & Security -> Developer Mode, turn Developer Mode on, and accept the restart. iOS 15 does not have this setting and the helper does not require it there.
- After the restart, unlock the phone, confirm Turn On in the Developer Mode prompt, and enter the passcode again.
- Reconnect the cable and run the doctor described below.
Developer Mode is an iOS security control and cannot be bypassed from Ubuntu. If the Developer Mode setting is absent, connect the phone once to a physical Mac and use Apple's supported Xcode or Apple Configurator device preparation, then return to Ubuntu after the phone has restarted and Developer Mode is on.
Trust, pairing, and Developer Mode can be cleared by device resets, iOS updates, or privacy/location resets. Repeat these steps if the doctor reports that the previously working device is no longer ready.
Apple Developer Setup
This requires the paid Truck Wash ApS Apple Developer team and a user permitted to manage certificates, identifiers, and devices.
Register the device and debug App ID
- Connect and unlock the iPhone, then get its UDID locally with
idevice_id -l. Treat the full UDID as sensitive operational data: do not commit it or paste it into ordinary build logs. - In Apple Developer Certificates, Identifiers & Profiles -> Devices, add the iPhone using that UDID.
- Under Identifiers, create an explicit App ID for
io.truckwash.app.debug. - Enable only capabilities required by the current Xcode project. Do not copy unrelated production entitlements into the debug App ID.
Create the certificate and development profile
- Create a dedicated Apple Development certificate for CI device-debug signing. Keep its private key under the team's normal credential controls.
- Export the certificate and private key together as a password-protected
.p12file. - Create an iOS App Development provisioning profile that selects:
- App ID
io.truckwash.app.debug - The dedicated Apple Development certificate
- Every approved physical test iPhone, including the cable-connected device
- App ID
- Download the
.mobileprovisionfile. - Confirm the profile has not expired, includes the intended device UDIDs, and
grants
get-task-allow=true. An App Store or ad-hoc profile is not valid for this workflow.
Base64-encode both files without line wrapping before adding them to GitHub. On Ubuntu, for example:
base64 -w 0 TruckWashDebug.p12 > TruckWashDebug.p12.base64
base64 -w 0 TruckWashDebug.mobileprovision > TruckWashDebug.mobileprovision.base64
Store the encoded values in GitHub immediately, verify one successful build,
then securely remove the local .p12, profile, encoded copies, CSR, and any
other private-key intermediates that are no longer required. Never commit
signing files or their encoded contents.
GitHub Environment And Dispatch Approval
Create a repository environment named mobile-device-debug. Store the debug
signing configuration only in that environment. Required environment reviewers
are not available for this private repository's current GitHub plan, so the
manual workflow_dispatch inputs are the signing approval boundary.
Restrict the environment's custom deployment branches to the exact master
branch. The checked-in workflow also refuses any other workflow ref. This keeps
signing secrets behind the reviewed workflow on master, while source_ref
can still select a separately inspected same-repository commit to build.
Add these environment variables exactly:
APPLE_TEAM_IDIOS_DEBUG_BUNDLE_ID=io.truckwash.app.debugIOS_DEBUG_API_URL=https://api-v2.truckwash.io/master/api
Add these environment secrets exactly:
IOS_DEBUG_CERTIFICATE_BASE64: base64 of the password-protected.p12IOS_DEBUG_CERTIFICATE_PASSWORD: password used to export the.p12IOS_DEBUG_PROVISION_PROFILE_BASE64: base64 of the development.mobileprovisionIOS_DEBUG_ALLOWED_UDIDS: newline-delimited UDIDs for every device that the profile is expected to contain
The workflow generates and masks a new random password for its temporary macOS
keychain on every run. Do not create or store an
IOS_DEBUG_KEYCHAIN_PASSWORD secret.
Do not reuse the mobile-store-production distribution secrets. The debug job
must use an Apple Development certificate and iOS App Development profile; the
existing io.truckwash.app App Store workflow remains unchanged.
The person dispatching a run must inspect the intended commit first. Do not dispatch when:
- The exact 40-character SHA is not the branch, tag, or commit intended.
- The source comes from a fork or another repository.
- The requested change is not appropriate to sign for a physical device.
- The signing profile is expired or no longer covers the intended device.
The workflow independently resolves source_ref inside this repository and
requires it to equal expected_sha. It also requires the exact typed
confirmation SIGN IOS DEBUG IPA. A missing/mismatched SHA or confirmation
stops the unprivileged resolver before the environment signing secrets are used.
Build And Download A Debug IPA
-
Open Actions -> iOS Device Debug IPA -> Run workflow and keep Use workflow from set to
master. -
Inspect the intended commit and copy its complete 40-character SHA.
-
Enter
source_ref. It may be a branch, tag, or commit in this repository and defaults tomaster. -
Enter the complete SHA as
expected_shaand enter the exact confirmationSIGN IOS DEBUG IPA. Submitting these inputs is approval to sign that source. -
The resolver pins
source_refinside this repository and verifies it equalsexpected_sha. A mismatch stops the run before signing. -
Wait for the signed macOS job to finish. It builds a Debug archive against
https://api-v2.truckwash.io/master/api, exports it with methoddevelopment, validates the embedded profile and app identity, and never uploads the result to App Store Connect. -
Download the
truck-wash-debug-<version>-<12-character-SHA>GitHub Actions artifact for the run. Keep its same-prefix.ipa,.dSYM.zip,manifest.json, andSHA256SUMStogether in one directory. -
From that directory, verify the download before connecting it to a device:
sha256sum --check SHA256SUMS
Do not install an artifact after a checksum failure. The manifest records the source ref and SHA, build/run numbers, bundle identity, stable API, minimum iOS, Xcode/Capacitor versions, signing method, and provisioning-profile expiration. It intentionally does not contain device UDIDs or secrets.
Artifacts are retained for seven days. Keep the zipped dSYM with any crash report from that build so a Mac/Xcode crash-symbolication path remains available.
Ubuntu Device Commands
Run commands from the repository root. The npm interface is:
npm run mobile:ios:device -- <command>
If the npm wrapper is unavailable, use the equivalent direct entrypoint:
node scripts/mobile/ios-device.mjs <command>
The helper uses USB devices only. With one connected iPhone, omit --udid.
With multiple devices connected, provide --udid ID; the command fails instead
of guessing. Normal output redacts full UDIDs.
Check readiness
Unlock the phone and run:
npm run mobile:ios:device -- doctor
The doctor verifies required host commands, USB discovery, pairing, activation, unlocked state, Developer Mode, and installation-proxy access. Resolve every reported failure before attempting an install.
For a specific connected device:
npm run mobile:ios:device -- doctor --udid DEVICE_UDID
Install or upgrade
Keep the downloaded artifact files together and run:
npm run mobile:ios:device -- install ./truck-wash-debug-VERSION-SHA.ipa --manifest ./manifest.json
The helper requires and verifies SHA256SUMS and the complete workflow
manifest, then inspects the IPA and its embedded profile. It rejects missing or
mismatched artifact metadata, the wrong repository/source/API/bundle/executable,
an App Store/ad-hoc or expired profile, get-task-allow=false, a profile
missing the connected UDID, or an invalid app payload before calling
ideviceinstaller.
If io.truckwash.app.debug is absent, the helper installs it. If it is already
present, the helper upgrades it and confirms the resulting version/build on the
phone. It never uninstalls or replaces io.truckwash.app.
Launch Truck Wash Debug manually from the iPhone Home Screen. Keep the phone online for the first launch so iOS can perform Apple's PPQ validation for the provisioning profile. A firewall, DNS filter, VPN, or captive portal that blocks Apple's validation service can prevent a correctly signed development app from opening.
Collect filtered logs
Start logging, then reproduce the issue on the phone:
npm run mobile:ios:device -- logs
npm run mobile:ios:device -- logs --output ./truck-wash-debug.log
The debug executable is deliberately named TruckWashDebug, distinct from the
production executable. The helper verifies that exact name and filters
idevicesyslog output for it. It streams child-tool output through UDID
redaction; --output files are written by the helper with mode 0600 after
redaction. Logs should make it possible to correlate the app with its source SHA
and stable API target without exposing signing secrets or full device IDs.
Copy crash reports
Create a destination directory and copy reports from the phone:
mkdir -p ./ios-crashes
npm run mobile:ios:device -- crashes ./ios-crashes
Crash retrieval always keeps the original reports on the iPhone. Preserve the matching IPA manifest and dSYM with each report.
Remove only the debug app
Uninstall requires the exact debug bundle ID as typed confirmation:
npm run mobile:ios:device -- uninstall --confirm io.truckwash.app.debug
The helper refuses to remove the production bundle or any other bundle ID.
Adding Devices And Renewing Signing
A provisioning profile is a snapshot. Registering another iPhone in Apple Developer does not update an already downloaded profile.
When adding a device:
- Obtain its UDID locally and register it in the Apple Developer portal.
- Regenerate the
io.truckwash.app.debugiOS App Development profile with the new and existing approved devices selected. - Replace
IOS_DEBUG_PROVISION_PROFILE_BASE64. - Add the UDID to the newline-delimited
IOS_DEBUG_ALLOWED_UDIDSsecret. - Dispatch a new build; an existing IPA does not gain access to the new device.
Monitor the profile expiration recorded in each artifact manifest and the Apple Development certificate expiration in the portal. Before either expires, create/renew the signing material, regenerate the profile, replace the affected GitHub secrets, and prove the result with a new build and real-device install. Revoked or expired signing material invalidates later installation and can stop an already installed development build from launching.
Troubleshooting
No device, device locked, or installation proxy unavailable
- Use a direct data-capable cable and avoid an unreliable hub.
- Unlock the iPhone, keep its screen awake, reconnect it, and rerun
doctor. - Close other tools that may be exclusively interacting with the device.
- Do not repeatedly retry installation while the doctor reports a lock/service failure.
Pairing or trust failure
- Unlock the phone and accept the Trust prompt.
- If no prompt appears and
doctorreports invalid pairing, use the explicit repair guidance printed by the helper, reconnect, and confirm trust again. - Device privacy resets and some iOS updates require a new trust decision.
Developer Mode is disabled or absent on iOS 16 or newer
- Enable it under Settings -> Privacy & Security -> Developer Mode, restart, and confirm after the reboot.
- If the switch is absent, use a Mac with Xcode or Apple Configurator for Apple's supported one-time preparation. There is no Ubuntu bypass.
IPA, profile, certificate, or UDID mismatch
- Confirm the IPA is from iOS Device Debug IPA, not Mobile Store Artifacts.
- Check
manifest.jsonforio.truckwash.app.debug, development signing, the intended source SHA, and a future profile expiration. - Regenerate the development profile when a device was added, a certificate was replaced, or the profile expired; then replace the GitHub secret and rebuild.
- Never suppress the helper's profile, entitlement, bundle, or checksum checks.
App installs but will not launch
- Keep the phone online for Apple's initial PPQ validation.
- Check whether VPN, DNS, firewall, captive-portal, or device-management policy blocks Apple developer-app verification.
- Confirm Developer Mode is still on and the certificate/profile has not expired or been revoked.
- Collect syslog and crash reports before reinstalling so evidence is preserved.
App reports No response was received
- Keep the API base set to
https://api-v2.truckwash.io/master/api. The barehttps://api-v2.truckwash.iohost is the public gateway, not the application API base. - Confirm
https://api-v2.truckwash.io/master/api/pingresponds before investigating the device or app. - Capacitor serves bundled iOS content from
capacitor://localhost. The stable API must returnAccess-Control-Allow-Origin: capacitor://localhostfor that exact origin, including authenticated preflight requests. - The signing workflow checks API reachability and this CORS contract before
compiling or signing. If it fails, deploy the backend CORS policy fix before
dispatching another IPA; do not replace the API URL or use an unsupported
HTTP/HTTPS
iosSchemeworkaround. - An already-built IPA starts using a corrected server-side CORS policy without modification. Build and install a higher version when recording a verified device-test result for the fix.
iOS beta or new major iOS version breaks device tools
- Record the device model, exact iOS version, helper error, source SHA, and IPA checksum.
- Update libimobiledevice only through a trusted package/source and rerun the doctor. Do not install arbitrary device images or disable signing checks.
- If compatibility remains broken, distribute through TestFlight or install and debug from a physical Mac with a compatible Xcode version.
Real-Device Acceptance Checklist
For the first setup, after signing changes, and after major iOS upgrades:
doctorpasses while the phone is unlocked.SHA256SUMSverifies and the manifest identifies the intended immutable SHA.- Truck Wash Debug installs as
io.truckwash.app.debugwhile the production app and its data remain unchanged. - Authentication, camera/QR permission, and foreground-location behavior work.
- Logs show the expected build/source context and stable API target.
- The debug app still launches and reaches the API after the cable is removed.
- A higher-numbered IPA upgrades the debug app without clearing its local state.
- Crash reports are copied without being deleted from the phone.
- The test record includes artifact checksum, source SHA, device model, iOS version, outcome, and any residual iOS/libimobiledevice compatibility risk.