--- id: mobile-apps/setting-up-a-phone-should-be-a-link-in-the-app title: 'Setting up a phone for calendar and contacts should be one link inside Officer, not a manual account form' theme: mobile-apps status: open created: 2026-08-27 date_source: stated order: 5 --- **The owner's shape for it, stated 2026-08-27:** open Calendar or Contacts in Officer, and the screen already knows this phone is not set up. One link. Clicking it generates the profile on the server, the phone installs it, and everything works. No typing a server address, no username, no password. The endpoint for that already exists — `POST /api/caldav/provision/ios` in the caldav plugin — and it is **unreachable from the product**: nothing in the UI calls it. The "Sync to your phone" modal offers only manual app passwords. So the smooth path is built and hidden, and the path a new person actually walks is the one with four fields in it. ## What is known, from an evening of it on 2026-08-27 **The profile does not install on iOS 26.6.** Every attempt returned *Profile Error — Invalid Profile*. What was ruled out, each with evidence rather than reasoning: - **Delivery works.** Fetched over the public URL from another machine: `200`, `application/x-apple-aspen-config`, 2,491 bytes, parses cleanly with `plistlib`. - **The phone really received it.** `claimIosProfile` now logs, and it logged `ios profile claimed — 0 still pending` on the attempt that failed. iOS rejected the CONTENTS. - **The server side is correct.** The principal advertises `addressbook-home-set`, `/1/contacts/` is typed as an addressbook, and the manually-added accounts sync fine against the same URLs. - **`PayloadScope` was removed** (Apple documents it as macOS-only). It did not fix it. Worth stating plainly because it was briefly believed to have — the calendar had started working at the same moment for an unrelated reason: the owner had added the account by hand. So the remaining suspect is the payload itself — some key iOS 26 refuses, or unsigned profiles being rejected outright, which would make `DAV_PROFILE_SIGN_CERT`/`KEY` a precondition rather than a nicety. **Nothing has been tested against a signed profile.** That is the first experiment. ## The second problem, and it does not go away with a working profile **iOS folds CardDAV into an existing CalDAV account with the same server and username.** Adding the calendar by hand produced one "Officer" account offering Calendars and Reminders, with no Contacts toggle anywhere — and the server confirmed nothing had ever requested `/dav/1/contacts/`. The workaround that worked was adding CardDAV as a separate account with a DIFFERENT description. `ios-profile.ts` already carries a comment claiming a single two-payload profile sidesteps this. On 26.6 it does not, and the comment should be corrected when this is picked up rather than trusted. A `kinds` option was added to `buildPlist` and `only: 'caldav' | 'carddav'` to the route so the two accounts can be split across two profiles — written, never verified, because the profile itself never installed. ## What "already knows this phone is not set up" needs Nothing exists for it today. The server can see whether a device has ever authenticated — `lastUsedAt` on `dav_app_passwords` — which is enough for "no phone has ever synced", but not for "THIS phone has not", since a browser cannot identify the device it is being read on. Worth deciding whether the weaker signal is enough before building the stronger one. ## Related - `plugin-infrastructure/a-plugin-s-own-routes-lose-their-request-body` — found while debugging this. - The manual path works and is what the owner is using: app password from the modal, then CalDAV and CardDAV added by hand as two accounts with different descriptions.