Files
pleno-vue/docs/ios-device-debug.md
T
Jeppe B 8ddac065c6 Guard iOS debug signing with live CORS (#181)
Verify the stable API and exact Capacitor iOS CORS contract before signing device-debug IPAs, with regression coverage and troubleshooting guidance.
2026-07-20 14:37:17 +02:00

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

  1. Connect the iPhone directly with a data-capable USB cable.
  2. Unlock the phone and keep it awake. Tap Trust if iOS asks whether to trust this computer, then enter the device passcode.
  3. 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.
  4. After the restart, unlock the phone, confirm Turn On in the Developer Mode prompt, and enter the passcode again.
  5. 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

  1. 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.
  2. In Apple Developer Certificates, Identifiers & Profiles -> Devices, add the iPhone using that UDID.
  3. Under Identifiers, create an explicit App ID for io.truckwash.app.debug.
  4. 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

  1. Create a dedicated Apple Development certificate for CI device-debug signing. Keep its private key under the team's normal credential controls.
  2. Export the certificate and private key together as a password-protected .p12 file.
  3. 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
  4. Download the .mobileprovision file.
  5. 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_ID
  • IOS_DEBUG_BUNDLE_ID=io.truckwash.app.debug
  • IOS_DEBUG_API_URL=https://api-v2.truckwash.io/master/api

Add these environment secrets exactly:

  • IOS_DEBUG_CERTIFICATE_BASE64: base64 of the password-protected .p12
  • IOS_DEBUG_CERTIFICATE_PASSWORD: password used to export the .p12
  • IOS_DEBUG_PROVISION_PROFILE_BASE64: base64 of the development .mobileprovision
  • IOS_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

  1. Open Actions -> iOS Device Debug IPA -> Run workflow and keep Use workflow from set to master.

  2. Inspect the intended commit and copy its complete 40-character SHA.

  3. Enter source_ref. It may be a branch, tag, or commit in this repository and defaults to master.

  4. Enter the complete SHA as expected_sha and enter the exact confirmation SIGN IOS DEBUG IPA. Submitting these inputs is approval to sign that source.

  5. The resolver pins source_ref inside this repository and verifies it equals expected_sha. A mismatch stops the run before signing.

  6. Wait for the signed macOS job to finish. It builds a Debug archive against https://api-v2.truckwash.io/master/api, exports it with method development, validates the embedded profile and app identity, and never uploads the result to App Store Connect.

  7. Download the truck-wash-debug-<version>-<12-character-SHA> GitHub Actions artifact for the run. Keep its same-prefix .ipa, .dSYM.zip, manifest.json, and SHA256SUMS together in one directory.

  8. 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:

  1. Obtain its UDID locally and register it in the Apple Developer portal.
  2. Regenerate the io.truckwash.app.debug iOS App Development profile with the new and existing approved devices selected.
  3. Replace IOS_DEBUG_PROVISION_PROFILE_BASE64.
  4. Add the UDID to the newline-delimited IOS_DEBUG_ALLOWED_UDIDS secret.
  5. 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 doctor reports 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.json for io.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 bare https://api-v2.truckwash.io host is the public gateway, not the application API base.
  • Confirm https://api-v2.truckwash.io/master/api/ping responds before investigating the device or app.
  • Capacitor serves bundled iOS content from capacitor://localhost. The stable API must return Access-Control-Allow-Origin: capacitor://localhost for 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 iosScheme workaround.
  • 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:

  • doctor passes while the phone is unlocked.
  • SHA256SUMS verifies and the manifest identifies the intended immutable SHA.
  • Truck Wash Debug installs as io.truckwash.app.debug while 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.

References