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:
Paul Nothaft
2026-08-29 12:56:42 +02:00
parent fa7253a64c
commit b4d44b2731
6 changed files with 257 additions and 85 deletions
+67 -38
View File
@@ -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.
---