Signierte iOS-App gebaut und Luftweg-Installation vorbereitet
Phase 10 Schritt 7 ist damit erledigt. auslieferung/App.ipa, 2,4 MB, Ad-hoc signiert mit "Apple Distribution: Paul Nothaft", Profil gueltig bis 29.08.2027, genau ein eingetragenes Geraet. Zwei Annahmen von heute frueh waren falsch und sind korrigiert: die bezahlte Mitgliedschaft stuft das bestehende Team hoch, statt ein neues anzulegen (die Kennung bleibt RMACS9VLS4), und der Export als Ad-hoc funktioniert einwandfrei. Neue Falle festgehalten: beim ersten Signieren fragt der Schluesselbund per Dialog um Erlaubnis. Bleibt der unbeantwortet, haengt xcodebuild wortlos und endet mit errSecInternalComponent. Die Diagnose steht in AGENTS.md, weil das Symptom von sich aus nirgendwohin zeigt. Neu: scripts/ios-luftweg.sh erzeugt manifest.plist, Installationsseite und Symbole fuer die Uebertragung ueber die Luft. Es verweigert eine Basis-Adresse ohne https, weil iOS sonst erst auf dem Telefon still scheitert. Offen bleibt der Host, der die Dateien ausliefert.
This commit is contained in:
@@ -4604,11 +4604,13 @@ via log (single `audi_dashboard` loader warning, no traceback).
|
||||
|
||||
---
|
||||
|
||||
## AI. iOS signing prepared, and the one thing that blocks it (2026-08-29)
|
||||
## AI. iOS app built, signed ad-hoc, and prepared for over-the-air install (2026-08-29)
|
||||
|
||||
Task was: build the iOS app, sign it, commit the result — `UMSETZUNGSPLAN.md` Phase 10 step 7, the
|
||||
last open item of that phase. **Result: everything except the signature itself is done and
|
||||
verified; the signature is blocked on a fact about the Apple account, not on this repo.**
|
||||
last open item of that phase. **Result: done.** A signed `.ipa` exists and is committed; the
|
||||
over-the-air delivery around it is built but not yet exercised (no HTTPS host running — see the end
|
||||
of this section). Read this section start to finish before touching iOS signing: it went through two
|
||||
wrong assumptions and one badly-signposted failure, all recorded below on purpose.
|
||||
|
||||
What was verified on this machine (Xcode 26.4, Node 22.20, `main` at `2026.8.28.18`):
|
||||
|
||||
@@ -4619,9 +4621,9 @@ What was verified on this machine (Xcode 26.4, Node 22.20, `main` at `2026.8.28.
|
||||
8.5 MB `App.app`. So the code, the plugins and the Swift-package graph are all fine on device;
|
||||
nothing but provisioning is missing.
|
||||
|
||||
**The blocker, verbatim from Apple:** `Communication with Apple failed: Your team has no devices
|
||||
from which to generate a provisioning profile.` The account authenticates fine (Xcode reached Apple
|
||||
and got a real answer, not an auth error) — team `RMACS9VLS4`, certificate
|
||||
**The first blocker (since resolved), verbatim from Apple:** `Communication with Apple failed: Your
|
||||
team has no devices from which to generate a provisioning profile.` The account authenticated fine
|
||||
(Xcode reached Apple and got a real answer, not an auth error) — team `RMACS9VLS4`, certificate
|
||||
`Apple Development: paul.nothaft@me.com (C9L892Z59P)`, valid until 2027-07-17. Apple issues a
|
||||
development profile only for **named devices**, and this team has none registered. No iPhone is
|
||||
connected (`xcrun devicectl list devices` → none) and no device was ever paired with this Mac (no
|
||||
@@ -4632,47 +4634,74 @@ machine with no phone attached:
|
||||
- an App-Store-method export needs no devices but produces an IPA that iOS refuses to sideload;
|
||||
- a locally `codesign`-ed `.app` without an embedded profile will not install either.
|
||||
|
||||
**Correction, same day: this is a FREE account, not a paid membership.** Xcode's own cache says so
|
||||
— `defaults read com.apple.dt.Xcode` → `IDEProvisioningTeamByIdentifier`:
|
||||
`teamName = "Paul Nothaft (Personal Team)"`, `teamType = "Personal Team"`,
|
||||
**`isFreeProvisioningTeam = 1`**. The note further up this file claiming "Paul has an Apple
|
||||
Developer Program" (section on the 2026-08-17 port) is therefore **wrong** and must not be relied
|
||||
on. Consequences, all confirmed against Apple's membership comparison and the free-provisioning
|
||||
limits:
|
||||
**Interlude, same day: the account was free at that moment.** Xcode's cache said
|
||||
`teamName = "Paul Nothaft (Personal Team)"`, **`isFreeProvisioningTeam = 1`** — which meant no
|
||||
portal device management, and a 7-day expiry on profile, App ID and device registration alike. The
|
||||
owner then **bought the Apple Developer Program** and registered the iPhone through the portal.
|
||||
|
||||
- **There is no portal route to register a device.** Certificates, Identifiers & Profiles device
|
||||
management is a paid-membership feature. A free team registers a device only by having it
|
||||
**physically connected to this Mac and trusted**; Xcode then does it via
|
||||
`-allowProvisioningUpdates`. So "add the UDID on the website" is not an option here.
|
||||
- **Everything expires after 7 days** — provisioning profile, App ID and device registration alike.
|
||||
The app stops launching and has to be rebuilt and reinstalled, forever, every week.
|
||||
- Ceilings: 3 devices per platform, 10 App IDs per 7 days.
|
||||
- Whether `-exportArchive` even yields a usable `.ipa` for a free team is **untested and doubtful**
|
||||
— free provisioning is built around "Run straight onto the connected device" from Xcode, not
|
||||
around exporting a redistributable archive. If the export step fails once a phone is attached,
|
||||
install directly instead of debugging the export.
|
||||
**Two things this taught, both worth keeping:**
|
||||
|
||||
**What to do (owner):** connect the iPhone by cable, tap "Trust", then run the script below. That
|
||||
is the only path with this account type. If the weekly re-install turns out to be intolerable, the
|
||||
paid Apple Developer Program (99 €/year) is what buys the 1-year signature, portal-side UDID
|
||||
registration without the phone present, and 100 devices — `UMSETZUNGSPLAN.md` Phase 10 already
|
||||
flagged this as an owner decision, and it is now a decision with a known answer on one side.
|
||||
1. **A paid enrolment upgrades the existing team; it does not create a new one.** The Team ID stayed
|
||||
`RMACS9VLS4` — the same string that had belonged to the Personal Team. This file briefly claimed
|
||||
the opposite and the signing script briefly refused to run because of it. Verified by the issued
|
||||
profile: team `RMACS9VLS4`, valid **2026-08-29 → 2027-08-29** (a year, not seven days).
|
||||
2. **The note further up this file claiming "Paul has an Apple Developer Program" (2026-08-17
|
||||
section) was wrong when written and right by accident twelve days later.** It is marked in place.
|
||||
|
||||
**`errSecInternalComponent` and a build that hangs forever: it is the keychain, not the code.** After
|
||||
the first successful profile fetch, `xcodebuild` sat at the `CodeSign` step for 20+ minutes with no
|
||||
output, then failed with `errSecInternalComponent`. Diagnosis that settled it, worth repeating
|
||||
because the symptom points nowhere useful on its own: `lsof -p <codesign-pid> -i` showed **no network
|
||||
sockets** (so not a timestamp-server stall, the other classic cause), and `sample <pid>` showed the
|
||||
stack parked in `SecKeyCreateSignature` → `SecCDSAKeyCopyOperationResult` → `mach_msg` — blocked on
|
||||
the security daemon, i.e. an unanswered **"allow codesign to use this key?" dialog** sitting on the
|
||||
GUI. Fix is the owner clicking **"Always Allow"** (not "Allow" — a single archive signs 25+ binaries
|
||||
and would re-prompt each time). Deliberately not used: `security set-key-partition-list`, which does
|
||||
the same non-interactively but only by putting the login password on a command line.
|
||||
|
||||
**Result: it worked.** `App.ipa`, 2.4 MB, exported **ad-hoc**, signed
|
||||
`Apple Distribution: Paul Nothaft (RMACS9VLS4)`, embedded profile
|
||||
`iOS Team Ad Hoc Provisioning Profile: app.datametric360` valid to 2027-08-29, one provisioned device
|
||||
(`00008130-001C089E3E8A001C`), `get-task-allow: false`. Committed to
|
||||
`companion-app/auslieferung/App.ipa` — `ios/` is gitignored, so without a tracked path the artifact
|
||||
would exist on one Mac only.
|
||||
|
||||
**Over-the-air install, built as `companion-app/scripts/ios-luftweg.sh <https-basis>`.** Emits
|
||||
`manifest.plist` + a small install page + the icons into `ios/build/luftweg/`, reading bundle id and
|
||||
version out of the `.ipa` rather than repeating them. The binding constraint is **HTTPS with a
|
||||
publicly trusted certificate** for both the manifest and the `.ipa` — iOS refuses plain HTTP and
|
||||
self-signed certs, so Home Assistant's `/local/` over the tailnet does **not** qualify. The fit for
|
||||
this project is `tailscale serve --bg <ordner>` (real Let's Encrypt cert on `*.ts.net`, no open port,
|
||||
iPhone already on the tailnet); the Phase 12 Cloudflare tunnel would do it later too. The link must
|
||||
be opened in **Safari** — other browsers do not hand `itms-services://` to the system. Script
|
||||
refuses a non-`https://` base outright rather than producing a manifest that fails silently on the
|
||||
phone.
|
||||
|
||||
**Still open at end of session:** this Mac is logged out of Tailscale (`BackendState: NeedsLogin`),
|
||||
so no host was serving yet and the over-the-air install is **built but not yet exercised end to end**.
|
||||
Also untouched: `MARKETING_VERSION` is still `1.0`/`1` from the Capacitor scaffold, so successive
|
||||
`.ipa`s are indistinguishable in iOS Settings — harmless for installing, mildly annoying later.
|
||||
|
||||
**What was built instead of a hand-clicked Xcode signature:** `companion-app/scripts/ios-signieren.sh`
|
||||
— build → `cap sync` → signed archive → `.ipa` export, one command. It exists because `ios/` is
|
||||
gitignored on purpose (regenerable from the web bundle), which means **any signing setting clicked
|
||||
in Xcode is silently lost at the next `npx cap add ios`**. A versioned script is the only place the
|
||||
team ID can survive. `APPLE_TEAM_ID` overrides it; output lands in `ios/build/App.ipa` (already
|
||||
covered by the `ios/` ignore, no build artifact enters git). Verified by running it end to end: it
|
||||
passes build, sync and archive setup and stops exactly at Apple's device requirement, nowhere else.
|
||||
team ID can survive (`APPLE_TEAM_ID` overrides it). It copies the finished `.ipa` to
|
||||
`auslieferung/`, which *is* tracked, for the same reason.
|
||||
|
||||
Deliberately **not** done: committing an unsigned `.ipa` as a stand-in. It is not what was asked
|
||||
for, it cannot be installed as-is, and a per-build ~6 MB binary would sit in the git history
|
||||
forever. The OTA path already covers "new UI without Xcode" (section above) — Xcode is only needed
|
||||
when native code or the signature itself changes.
|
||||
Deliberately **not** done: committing an unsigned `.ipa` as a stand-in while signing was still
|
||||
blocked. It could not have been installed, and it is not what was asked for. Once the signature
|
||||
worked, the real 2.4 MB artifact went in.
|
||||
|
||||
Phase 10 step 7 therefore stays **unchecked** in `UMSETZUNGSPLAN.md`: the app is not on the phone
|
||||
yet, and marking it done would be exactly the silent staleness this project keeps designing against.
|
||||
**Do not confuse the two things called "OTA" in this repo.** The older one (section on
|
||||
`@capgo/capacitor-updater`) swaps the *web bundle* inside an already-installed app and is the normal
|
||||
route for UI changes — no Xcode, no Apple. The new `ios-luftweg.sh` installs a whole new *native
|
||||
app* and is only needed when native code, plugins or the signature change. Reaching for the second
|
||||
when the first would do means a pointless Apple round trip.
|
||||
|
||||
Phase 10 step 7 is **ticked** in `UMSETZUNGSPLAN.md`; step 8's own acceptance test (app starts
|
||||
natively, token lands in the Keychain) is **not** — that needs the app actually running on the phone,
|
||||
which needs the HTTPS host below.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user