setting up a phone should be one link inside officer — open Calendar, the screen already knows this phone is not set up, click, done. the endpoint for it exists (POST /api/caldav/provision/ios) and nothing in the UI calls it, so the smooth path is built and hidden while the path a new person walks has four fields. it also does not currently work: the profile is refused by iOS 26.6 as "Invalid Profile". the todo records what was RULED OUT with evidence rather than reasoning, because that is the expensive part to redo — delivery works, the phone really received it (the claim is logged now), the server side is correct, and removing PayloadScope did not fix it. signing is untested and is the first experiment. second: a plugin with a sidecar proxy loses the request body on its own routes. createSidecarProxy registers the whole mount prefix and bodyParser skips it, which is right for forwarded traffic and load-bearing for uploads — but it covers the plugin's own routes too. caldav works around it locally; the real fix is the proxy claiming only the sub-path it serves, which is shared machinery and not a thing to change with a phone waiting.
3.7 KiB
id, title, theme, status, created, date_source, order
| id | title | theme | status | created | date_source | order |
|---|---|---|---|---|---|---|
| mobile-apps/setting-up-a-phone-should-be-a-link-in-the-app | Setting up a phone for calendar and contacts should be one link inside Officer, not a manual account form | mobile-apps | open | 2026-08-27 | stated | 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 withplistlib. - The phone really received it.
claimIosProfilenow logs, and it loggedios profile claimed — 0 still pendingon 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. PayloadScopewas 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.