every signed profile was rejected with "Invalid Profile". `openssl smime -verify` reported success the whole time, which is exactly why it took so long to see: the signature was valid over the WRONG BYTES. without `-binary`, openssl treats the input as TEXT — it canonicalises line endings and adds MIME headers before signing — so what iOS unwraps is no longer the plist that was written. this was already found and written down in officer-oas/enroll.ts on 2026-08-20, four wrong endings into a hunt that was never about the ending. this file was written without reading that one, and repeated it exactly. also reads OFFICERDEV_DAV_PROFILE_SIGN_* as well as the bare names, which is the convention every other consumer follows. on a converted install the signing certificate could be configured correctly and silently ignored, and the only symptom is a profile that says "Not Signed" for no visible reason. verified on a real iPhone (iOS 26.6): the profile installs, and the signed inner payload starts with <?xml with no MIME headers and no CRLF mangling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
Calendar
Calendars and contacts on your own machine, synced to your phone over CalDAV and CardDAV.
- Radicale, not a container. A ~2MB Python package, spawned on loopback by this plugin's sidecar. It was a Docker service only because the old app store made everything one.
- Per user. The sync door resolves an app password to one account and tells Radicale that account's id
is the principal;
[rights] type = owner_onlydoes the rest. Every user gets their own calendars and their own contacts, in their own directory, invisible to everyone else. - Phones work. DAVx5, iOS and Thunderbird, including RFC 6764 autodiscovery from a bare domain.
Two screens, one plugin
/calendar and /contacts. They are two places you go rather than two sections of one page, which is why
this is the first plugin to declare screens in its manifest — one sidecar, one permission, one install,
two routes and two dock tiles.
What it claims from the platform, and why
Four paths outside /api, above the account gate:
| Path | Why it cannot sit behind the session |
|---|---|
/dav/* |
DAVx5, iOS and Thunderbird send HTTP Basic on every request and have nowhere to put a JWT |
/dav/provision/*.mobileconfig |
Safari is handed a URL by the phone app and has no credential; the one-shot token in the path IS the authentication |
/.well-known/caldav |
RFC 6764 — probed with a bare domain before the client has any credential |
/.well-known/carddav |
the same, for contacts |
"Above the account gate" is not "open". The sync door demands a scoped DAV app password and resolves it to a user before anything reaches Radicale.
Every one of those is in the platform's RESERVED_ROOT, claimable only by a first-party plugin. A
third-party plugin is confined to /p/<publisher>/ so it can never shadow a core route.
Extracted from the platform on 2026-08-27
It was core from 2026-08-19. What moved: the sidecar, the four routers, the panel apps, the app-password table, and the settings form — which became a modal on the Calendar screen, because a plugin ships panels and cannot contribute a settings section.
Two things deliberately did not change. The table is still dav_app_passwords rather than
caldav_app_passwords: installs already hold real rows, and the platform's prefix convention is not worth
a data migration on a live machine. And the panel names still resolve through the platform's translation
catalogs, so the six locales they had as core screens survive.