APP PASSWORDS: one-scan setup — QR → Apple configuration profile on iPhone/iPad, one-click profile on Mac, collapsible step lists #318

Closed
opened 2026-09-28 08:42:39 +00:00 by kayg · 16 comments
Owner

Owner (2026-09-28, screenshot of Settings → Account → App passwords for calendar apps): 'is it 2025? i appreciate the detailed information that it displays but it should ideally be a qr code for mobile to scan and auto add the calendar? … add to apple calendar instructions should be on multiple lines… please use collapsible sections for those instructions. Basically have two sections: Mobile: QR CODE, Manual instructions. Desktop: Manual instructions (unless there's a way to automate this).'

Context: CalDAV discovery was fixed in f47399ad (PROPFIND on /.well-known/caldav). The Basic username is the immutable account ID by design (AppPasswordCreated.username), so renaming the account never breaks connected apps. The UI must explain this in one short line ('Your calendar apps sign in with your account ID, so renaming your account never disconnects them') instead of leaving the owner to wonder why it is not 'kayg'.

Build (reuse Settings components; opinionated and easy):

  1. After creating an app password, the ready panel shows two collapsible sections, iPhone & iPad and Mac, plus a small 'Other apps (Thunderbird, DAVx⁵, …)' section. Open by default: the section that matches the current device (user agent); the others are collapsed.
  2. iPhone & iPad: QR code. It encodes a short-lived, single-use HTTPS URL (e.g. 10 minutes, one download, bound to this app password) that serves an Apple configuration profile (.mobileconfig, MIME application/x-apple-aspen-config) with a CalDAV payload (com.apple.caldav.account: host calternal.cloud, port 443, SSL, principal URL /dav/, username = the account ID, password = the app password, account description 'calternal'). Scanning opens Safari → 'Profile downloaded' → Settings → Install. The calendar appears with no typing. Sign the profile with the instance's TLS certificate/key if practical (otherwise it shows 'Not verified'; document the trade-off). The QR is shown only in the ready panel (the password is shown once), and the URL dies after one use or 10 minutes. Security: the URL token is random (≥128 bits), single-use, rate-limited, never logged, and served with no-store caching; the profile embeds the secret, so it is never cached or indexed.
  3. Mac: one click. A Download profile for Mac button serves the same .mobileconfig. Double-click → System Settings → Profiles → Install. That is the desktop automation.
  4. Manual steps in each section, as a numbered list, one step per line (never a paragraph), with the values as copy chips inline: iPhone Settings → Apps → Calendar → Calendar Accounts → Add Account → Other → Add CalDAV Account; Mac Calendar → Settings → Accounts → + → Other CalDAV Account → Manual; Thunderbird; DAVx⁵ (Android).
  5. Accessibility: the QR has a text alternative ('Scan with your iPhone camera to add calternal to Calendar') plus the manual path; the sections are real disclosure buttons; the steps are an ordered list.
  6. Tests: profile XML validity (plist), the payload fields, token single-use and expiry, and an adversarial round (replay, brute force, other users' tokens, oversized requests).
    Deep link: Settings → Account → App passwords is deep-linkable (§33).
    Evidence: screenshots (desktop, phone; light and dark) and a real install on an iPhone and a Mac by the owner (the orchestrator will ask).
Owner (2026-09-28, screenshot of Settings → Account → App passwords for calendar apps): 'is it 2025? i appreciate the detailed information that it displays but it should ideally be a qr code for mobile to scan and auto add the calendar? … add to apple calendar instructions should be on multiple lines… please use collapsible sections for those instructions. Basically have two sections: **Mobile**: QR CODE, Manual instructions. **Desktop**: Manual instructions (unless there's a way to automate this).' Context: CalDAV discovery was fixed in f47399ad (PROPFIND on /.well-known/caldav). The Basic username is the immutable account ID by design (AppPasswordCreated.username), so renaming the account never breaks connected apps. The UI must explain this in one short line ('Your calendar apps sign in with your account ID, so renaming your account never disconnects them') instead of leaving the owner to wonder why it is not 'kayg'. Build (reuse Settings components; opinionated and easy): 1. After creating an app password, the ready panel shows two **collapsible sections**, **iPhone & iPad** and **Mac**, plus a small 'Other apps (Thunderbird, DAVx⁵, …)' section. Open by default: the section that matches the current device (user agent); the others are collapsed. 2. **iPhone & iPad: QR code.** It encodes a short-lived, single-use HTTPS URL (e.g. 10 minutes, one download, bound to this app password) that serves an **Apple configuration profile** (`.mobileconfig`, MIME `application/x-apple-aspen-config`) with a CalDAV payload (`com.apple.caldav.account`: host calternal.cloud, port 443, SSL, principal URL `/dav/`, username = the account ID, password = the app password, account description 'calternal'). Scanning opens Safari → 'Profile downloaded' → Settings → Install. The calendar appears with no typing. **Sign the profile** with the instance's TLS certificate/key if practical (otherwise it shows 'Not verified'; document the trade-off). The QR is shown only in the ready panel (the password is shown once), and the URL dies after one use or 10 minutes. Security: the URL token is random (≥128 bits), single-use, rate-limited, never logged, and served with no-store caching; the profile embeds the secret, so it is never cached or indexed. 3. **Mac: one click.** A **Download profile for Mac** button serves the same `.mobileconfig`. Double-click → System Settings → Profiles → Install. That is the desktop automation. 4. **Manual steps** in each section, as a **numbered list, one step per line** (never a paragraph), with the values as copy chips inline: iPhone Settings → Apps → Calendar → Calendar Accounts → Add Account → Other → Add CalDAV Account; Mac Calendar → Settings → Accounts → + → Other CalDAV Account → Manual; Thunderbird; DAVx⁵ (Android). 5. Accessibility: the QR has a text alternative ('Scan with your iPhone camera to add calternal to Calendar') plus the manual path; the sections are real disclosure buttons; the steps are an ordered list. 6. Tests: profile XML validity (plist), the payload fields, token single-use and expiry, and an adversarial round (replay, brute force, other users' tokens, oversized requests). Deep link: Settings → Account → App passwords is deep-linkable (§33). Evidence: screenshots (desktop, phone; light and dark) and a real install on an iPhone and a Mac by the owner (the orchestrator will ask).
Author
Owner

Starting work on job/app-pw-setup from dev at b6f31d95f7. I am tracing the existing app-password, auth API, settings, OpenAPI generation, and adversarial-test paths before implementation.

Starting work on job/app-pw-setup from dev at b6f31d95f7875c1c67ee5101c7a5b82253173487. I am tracing the existing app-password, auth API, settings, OpenAPI generation, and adversarial-test paths before implementation.
Author
Owner

Finding: app_passwords stores only Argon2 hashes, and the create route returns the secret once. I will send that secret in the authenticated profile-link request, verify that it belongs to the requested password ID, and hold the profile bytes in memory for 10 minutes. The repository has no profile-signing identity or PKCS#7 flow; I will serve an unsigned profile and state that Apple may show “Not Verified”.

Finding: `app_passwords` stores only Argon2 hashes, and the create route returns the secret once. I will send that secret in the authenticated profile-link request, verify that it belongs to the requested password ID, and hold the profile bytes in memory for 10 minutes. The repository has no profile-signing identity or PKCS#7 flow; I will serve an unsigned profile and state that Apple may show “Not Verified”.
Author
Owner

Owner (2026-09-28): 'Does Mac not have a setup link where I can just click on a link and it opens the correct settings page and automatically the server, username and password are filled? … maybe it's called profiles.' Yes: this issue's .mobileconfig flow is that. Add to the job's research and report (web search, Apple primary docs only):

  1. The current macOS 27 / iOS 27 install flow for a downloaded profile (the 'Profile Downloaded' notification → System Settings → General → Device Management → Install), including whether Safari and Chrome on macOS both hand the file to System Settings, and what the user sees.
  2. The exact com.apple.caldav.account payload keys from Apple's Device Management documentation (CalDAVHostName, CalDAVPort, CalDAVUseSSL, CalDAVPrincipalURL, CalDAVUsername, CalDAVPassword, CalDAVAccountDescription), and whether the password can be embedded without the user retyping it.
  3. Signing: how to sign with the instance's TLS certificate (CMS/PKCS#7), how 'Verified' vs 'Unverified' shows, and whether signing is worth doing for v1.
  4. Whether a URL scheme or deep link can open the install screen directly after the download.
    The orchestrator will install the generated profile on the macOS test VM (calternal-macos) before the owner sees it, so the job must produce a downloadable profile from a local server.
Owner (2026-09-28): 'Does Mac not have a setup link where I can just click on a link and it opens the correct settings page and automatically the server, username and password are filled? … maybe it's called profiles.' Yes: this issue's .mobileconfig flow is that. Add to the job's research and report (web search, Apple primary docs only): 1. The **current macOS 27 / iOS 27 install flow** for a downloaded profile (the 'Profile Downloaded' notification → System Settings → General → Device Management → Install), including whether Safari and Chrome on macOS both hand the file to System Settings, and what the user sees. 2. The exact **com.apple.caldav.account payload keys** from Apple's Device Management documentation (CalDAVHostName, CalDAVPort, CalDAVUseSSL, CalDAVPrincipalURL, CalDAVUsername, CalDAVPassword, CalDAVAccountDescription), and whether the password can be embedded without the user retyping it. 3. **Signing**: how to sign with the instance's TLS certificate (CMS/PKCS#7), how 'Verified' vs 'Unverified' shows, and whether signing is worth doing for v1. 4. Whether a **URL scheme or deep link** can open the install screen directly after the download. The orchestrator will install the generated profile on the macOS test VM (calternal-macos) before the owner sees it, so the job must produce a downloadable profile from a local server.
Author
Owner

Post-merge web tests found one font-token violation in the new profile panel: AppPasswordsGroup.svelte:462 used font-weight: 600, and font-weight-token.test.ts rejected that numeric declaration. I am changing the heading to the shared title-weight role and will rerun the web suite.

Post-merge web tests found one font-token violation in the new profile panel: `AppPasswordsGroup.svelte:462` used `font-weight: 600`, and `font-weight-token.test.ts` rejected that numeric declaration. I am changing the heading to the shared title-weight role and will rerun the web suite.
Author
Owner

Completed

Built the QR-to-.mobileconfig setup for iPhone/iPad, a Mac profile download, and collapsible numbered setup steps for Mobile, Mac, and Other apps. The authenticated creation endpoint returns independent single-use links that expire after 10 minutes. The anonymous download endpoint consumes them atomically, checks that the app password is still active, and returns a CalDAV profile with private/no-store response headers. Tokens are stored as digests, requests are bounded and rate limited, and secrets are not logged.

Generated the OpenAPI contract and TypeScript API client. Added production E2E coverage and extended tests/adversarial for mismatched and cross-user IDs, malformed and oversized bodies, profile fields and headers, replay, concurrent consumption, rate limits, and CalDAV discovery.

Files

  • crates/calternal-auth/Cargo.toml, crates/calternal-auth/src/api.rs, crates/calternal-server/src/wire.rs, Cargo.lock
  • contracts/openapi.json, packages/api-client/src/generated.ts
  • apps/web/src/routes/settings/account/AccountSection.svelte, apps/web/src/routes/settings/account/AppPasswordsGroup.svelte, apps/web/e2e/app-passwords.mjs, apps/web/e2e/calendar.mjs, apps/web/package.json, bun.lock
  • tests/adversarial/attack2.py, tests/adversarial/setup.mjs

Decisions

  • There is no configured Apple signing identity, so the generated profile is unsigned. The UI explains that Apple may show “Not Verified.”
  • Pending profile data and one-use token digests use a bounded in-memory store. A server restart invalidates unused links; this avoids adding persistent storage for profile secrets.
  • Merged dev once before the final gates (362db784).

Verification

cargo fmt --check exited 0 with no output.

cargo clippy --all-targets -- -D warnings exited 0:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 52m 27s

cargo test exited 0. All executed workspace tests passed; tests marked ignored by their suites remained ignored. The run ended with:

   Doc-tests calternal_tags

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

bun run --cwd apps/web check:

Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/app-pw-setup/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run --cwd apps/web test:

 Test Files  110 passed (110)
      Tests  709 passed (709)
   Start at  12:47:10
   Duration  166.68s (transform 58%, environment 18%, import 13%, tests 8%, setup 3%)

Environment  |component| jsdom was created 29 times · 163.49s total, 30% of tracked time

Focused production E2E: app password e2e: QR, manual steps, Other apps, and Mac profile passed.

Post-merge adversarial round:

finish 200
login web 200 installation 200
cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ]
invite 200
second user finish 200
fixture passkey login requests: 30/30
---------- app_profiles ----------
server alive at end: True

==== ROUND 2 FINDINGS 0

==== ROUND 2 SLOW 0

The extended calendar E2E timed out twice at its existing .block.actual log-entry assertion, before reaching the app-password section. I left that expectation unchanged. The focused app-password E2E passes.

cargo clean output:

     Removed 20680 files, 15.6GiB total

Web build output was removed. The worktree is clean. Head: 47700e8883e29addb3ea504e5040b8b61fb3eeaf. Pushed job/app-pw-setup; push reported Everything up-to-date.

Production screenshots

Desktop light
Desktop dark
Phone light
Phone dark

## Completed Built the QR-to-`.mobileconfig` setup for iPhone/iPad, a Mac profile download, and collapsible numbered setup steps for Mobile, Mac, and Other apps. The authenticated creation endpoint returns independent single-use links that expire after 10 minutes. The anonymous download endpoint consumes them atomically, checks that the app password is still active, and returns a CalDAV profile with private/no-store response headers. Tokens are stored as digests, requests are bounded and rate limited, and secrets are not logged. Generated the OpenAPI contract and TypeScript API client. Added production E2E coverage and extended `tests/adversarial` for mismatched and cross-user IDs, malformed and oversized bodies, profile fields and headers, replay, concurrent consumption, rate limits, and CalDAV discovery. ## Files - `crates/calternal-auth/Cargo.toml`, `crates/calternal-auth/src/api.rs`, `crates/calternal-server/src/wire.rs`, `Cargo.lock` - `contracts/openapi.json`, `packages/api-client/src/generated.ts` - `apps/web/src/routes/settings/account/AccountSection.svelte`, `apps/web/src/routes/settings/account/AppPasswordsGroup.svelte`, `apps/web/e2e/app-passwords.mjs`, `apps/web/e2e/calendar.mjs`, `apps/web/package.json`, `bun.lock` - `tests/adversarial/attack2.py`, `tests/adversarial/setup.mjs` ## Decisions - There is no configured Apple signing identity, so the generated profile is unsigned. The UI explains that Apple may show “Not Verified.” - Pending profile data and one-use token digests use a bounded in-memory store. A server restart invalidates unused links; this avoids adding persistent storage for profile secrets. - Merged `dev` once before the final gates (`362db784`). ## Verification `cargo fmt --check` exited 0 with no output. `cargo clippy --all-targets -- -D warnings` exited 0: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 52m 27s ``` `cargo test` exited 0. All executed workspace tests passed; tests marked ignored by their suites remained ignored. The run ended with: ```text Doc-tests calternal_tags running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `bun run --cwd apps/web check`: ```text Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/app-pw-setup/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run --cwd apps/web test`: ```text Test Files 110 passed (110) Tests 709 passed (709) Start at 12:47:10 Duration 166.68s (transform 58%, environment 18%, import 13%, tests 8%, setup 3%) Environment |component| jsdom was created 29 times · 163.49s total, 30% of tracked time ``` Focused production E2E: `app password e2e: QR, manual steps, Other apps, and Mac profile passed`. Post-merge adversarial round: ```text finish 200 login web 200 installation 200 cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ] invite 200 second user finish 200 fixture passkey login requests: 30/30 ---------- app_profiles ---------- server alive at end: True ==== ROUND 2 FINDINGS 0 ==== ROUND 2 SLOW 0 ``` The extended calendar E2E timed out twice at its existing `.block.actual` log-entry assertion, before reaching the app-password section. I left that expectation unchanged. The focused app-password E2E passes. `cargo clean` output: ```text Removed 20680 files, 15.6GiB total ``` Web build output was removed. The worktree is clean. Head: `47700e8883e29addb3ea504e5040b8b61fb3eeaf`. Pushed `job/app-pw-setup`; push reported `Everything up-to-date`. ## Production screenshots ![Desktop light](https://git.kayg.org/attachments/73e0e468-7ca3-407b-8f9c-6feda95318fd) ![Desktop dark](https://git.kayg.org/attachments/ed71e7cf-d3e2-4002-aea5-2a261ba2f28f) ![Phone light](https://git.kayg.org/attachments/f0334c1b-849a-4cfc-9a8a-7dfbdb94c248) ![Phone dark](https://git.kayg.org/attachments/555b304f-0feb-41ae-85cd-7b81dd89504d)
Author
Owner

Owner (2026-09-28): 'i have an apple developer account, can we create signed profiles too so that they're trusted?' Plan (orchestrator): a signed .mobileconfig shows Verified instead of Unverified; the install flow is unchanged. Candidates: (1) a publicly trusted TLS certificate chain, e.g. a dedicated Let's Encrypt certificate used only for signing (do NOT hand the edge's TLS private key to the app container); (2) the owner's Apple Developer ID (Apple root; shows the developer name; official instance only). Implementation: PKCS#7/CMS detached-content signing (openssl smime -sign -nodetach -outform der equivalent, done in Rust with a permissive crate or a vetted subprocess), the signing cert + key loaded from an instance secret (never the repo, logs or issues), signing optional (unsigned when no key is configured), and self-hosters can configure their own. Verification on the macOS VM with both candidates before choosing; the orchestrator runs it. The owner may provide a .p12 for the Developer ID test.

Owner (2026-09-28): 'i have an apple developer account, can we create signed profiles too so that they're trusted?' Plan (orchestrator): a signed .mobileconfig shows **Verified** instead of Unverified; the install flow is unchanged. Candidates: (1) a publicly trusted TLS certificate chain, e.g. a dedicated Let's Encrypt certificate used only for signing (do NOT hand the edge's TLS private key to the app container); (2) the owner's Apple Developer ID (Apple root; shows the developer name; official instance only). Implementation: PKCS#7/CMS detached-content signing (`openssl smime -sign -nodetach -outform der` equivalent, done in Rust with a permissive crate or a vetted subprocess), the signing cert + key loaded from an instance secret (never the repo, logs or issues), signing optional (unsigned when no key is configured), and self-hosters can configure their own. **Verification on the macOS VM with both candidates before choosing**; the orchestrator runs it. The owner may provide a .p12 for the Developer ID test.
Author
Owner

Orchestrator review of 47700e88 (desktop 1440 dark + phone 390 dark): the flows exist (QR, Mac profile download, collapsible numbered steps, single-use 10-min links, 0 adversarial findings). Not accepted yet (visual):

  1. The password value overflows its container on desktop and phone: the long app password runs outside the card on both sides (it is clipped at the viewport edges). Long copyable values must wrap safely (overflow-wrap: anywhere in a monospace chip that can grow to several lines) or truncate in the middle with a Copy button; never overflow. The same applies to the account ID chip on narrow widths.
  2. Phone sheet header: the title shows 'DAVx⁵ on An…' (truncated, and it is the wrong title: it is the name of a collapsed section) and it overlaps the QR code under a blur. The sheet title must be the page's own title ('App password ready' or similar), never a section name, with no overlap (phone chrome rules).
  3. Contrast: 'Other apps (Thunderbird, DAVx⁵ …)' is nearly invisible (dark on dark); 'Done' and 'New app password' are low-contrast. Use the shared Pill/disclosure styles (the same as 'Mac' and 'Manual instructions').
  4. Evidence: add 820 px (tablet) crops, plus light theme for all three widths.
    The orchestrator is testing the actual profile install on the macOS VM in parallel and will post findings here.
Orchestrator review of 47700e88 (desktop 1440 dark + phone 390 dark): the flows exist (QR, Mac profile download, collapsible numbered steps, single-use 10-min links, 0 adversarial findings). **Not accepted yet (visual):** 1. **The password value overflows its container** on desktop and phone: the long app password runs outside the card on both sides (it is clipped at the viewport edges). Long copyable values must wrap safely (`overflow-wrap: anywhere` in a monospace chip that can grow to several lines) or truncate in the middle with a Copy button; never overflow. The same applies to the account ID chip on narrow widths. 2. **Phone sheet header:** the title shows 'DAVx⁵ on An…' (truncated, and it is the wrong title: it is the name of a collapsed section) and it overlaps the QR code under a blur. The sheet title must be the page's own title ('App password ready' or similar), never a section name, with no overlap (phone chrome rules). 3. **Contrast:** 'Other apps (Thunderbird, DAVx⁵ …)' is nearly invisible (dark on dark); 'Done' and 'New app password' are low-contrast. Use the shared Pill/disclosure styles (the same as 'Mac' and 'Manual instructions'). 4. **Evidence:** add 820 px (tablet) crops, plus light theme for all three widths. The orchestrator is testing the actual profile install on the macOS VM in parallel and will post findings here.
Author
Owner

Continuing the visual review fixes on branch job/app-pw-setup at 47700e8883 (original dev base b6f31d95f7). I am correcting the credential wrapping, phone sheet heading and control contrast, then capturing production screenshots at 390, 820 and 1440 px in light and dark.

Continuing the visual review fixes on branch job/app-pw-setup at 47700e8883e29addb3ea504e5040b8b61fb3eeaf (original dev base b6f31d95f7875c1c67ee5101c7a5b82253173487). I am correcting the credential wrapping, phone sheet heading and control contrast, then capturing production screenshots at 390, 820 and 1440 px in light and dark.
Author
Owner

Real macOS 27 verification of the Mac profile (orchestrator, calternal-macos VM, 2026-09-28), built from 47700e88 merged with the #323 CalDAV fixes, served over HTTPS (a temporary quick tunnel, test data only):

  • The one-use mac_url → Safari downloads calternal.mobileconfig (200); the link is consumed. Works.
  • The user sees, in order: (1) Safari 'Do you want to allow downloads on ?' (first time per site); (2) 'Profile Downloaded – review the profile in System Settings'; (3) System Settings → General → Device Management → 'calternal – Profile not installed. Double-click to review'; (4) the review sheet: 'calternal (Not Signed)', 'Adds calternal to Calendar.', Calendar Account, Server, Username; Install…; (5) because unsigned: 'The author of this profile is unknown. Make sure you trust the sender' → Install. No Mac password prompt.
  • After install: 'User · calternal · 1 setting', and the Calendar account signs in and syncs by itself (PROPFIND /dav/ 207 → principal 207 → home 207 → calendars 207). End to end: works.
    UI copy to update in this job: the Mac steps should mention the Safari 'Allow downloads' prompt and the exact path System Settings → General → Device Management, and say that the 'author unknown' confirmation is expected until profiles are signed (#318 signing plan). Keep it to one short line per step.
**Real macOS 27 verification of the Mac profile (orchestrator, calternal-macos VM, 2026-09-28)**, built from 47700e88 merged with the #323 CalDAV fixes, served over HTTPS (a temporary quick tunnel, test data only): - The one-use `mac_url` → Safari downloads `calternal.mobileconfig` (200); the link is consumed. **Works.** - The user sees, in order: (1) Safari 'Do you want to allow downloads on <host>?' (first time per site); (2) 'Profile Downloaded – review the profile in System Settings'; (3) System Settings → General → **Device Management** → 'calternal – Profile not installed. Double-click to review'; (4) the review sheet: 'calternal (Not Signed)', 'Adds calternal to Calendar.', Calendar Account, Server, Username; **Install…**; (5) because unsigned: 'The author of this profile is unknown. Make sure you trust the sender' → Install. **No Mac password prompt.** - After install: 'User · calternal · 1 setting', and the Calendar account **signs in and syncs by itself** (PROPFIND /dav/ 207 → principal 207 → home 207 → calendars 207). **End to end: works.** UI copy to update in this job: the Mac steps should mention the Safari 'Allow downloads' prompt and the exact path **System Settings → General → Device Management**, and say that the 'author unknown' confirmation is expected until profiles are signed (#318 signing plan). Keep it to one short line per step.
Author
Owner

Finding: OverlaySurface considered every h1–h6 a sticky title, including hidden headings in disclosures. The collapsed DAVx⁵ heading could therefore replace the Settings page title. Declared sheet-title headings now take priority; the web regression test covers this case. Apple’s current CalDAV payload docs also state that CalDAVPassword should only be used in encrypted profiles: https://developer.apple.com/documentation/devicemanagement/caldav. Apple documents profile signing as integrity protection, not password confidentiality: https://developer.apple.com/documentation/devicemanagement/configuring-multiple-devices-using-profiles and https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/profile-service/profile-service.html. The current one-use HTTPS download still places the password in the downloaded profile, so signing alone cannot close that confidentiality gap.

Finding: OverlaySurface considered every h1–h6 a sticky title, including hidden headings in disclosures. The collapsed DAVx⁵ heading could therefore replace the Settings page title. Declared sheet-title headings now take priority; the web regression test covers this case. Apple’s current CalDAV payload docs also state that CalDAVPassword should only be used in encrypted profiles: https://developer.apple.com/documentation/devicemanagement/caldav. Apple documents profile signing as integrity protection, not password confidentiality: https://developer.apple.com/documentation/devicemanagement/configuring-multiple-devices-using-profiles and https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/profile-service/profile-service.html. The current one-use HTTPS download still places the password in the downloaded profile, so signing alone cannot close that confidentiality gap.
Author
Owner

Apple platform research (primary sources):

Open security note: the current profile embeds the app password in an unencrypted plist. The one-use HTTPS download limits transport exposure, but signing alone does not satisfy Apple’s encrypted-profile guidance. The issue’s one-scan requirement asks for an embedded password, so I left the implementation in place and reported this trade-off for owner review.

Apple platform research (primary sources): - iOS/iPadOS: Apple says a website download shows “Profile Downloaded”; the user opens Settings, taps that item, then Install. A downloaded profile expires from the install queue after 8 minutes, and only one profile can be pending. I added these steps under the QR. https://support.apple.com/en-us/102400 - Mac: download and open the .mobileconfig file, then use System Settings → General → Device Management → Downloaded; double-click, review, and install. Apple does not document a Safari-versus-Chrome handoff difference, so the VM test should establish browser behavior. I updated the Mac instructions to this path. https://support.apple.com/guide/mac-help/mh35561/mac - Apple documents the CalDAV fields used here: CalDAVAccountDescription, CalDAVHostName, CalDAVPort, CalDAVUseSSL, CalDAVPrincipalURL, CalDAVUsername, and CalDAVPassword. It says CalDAVPassword should only be used in encrypted profiles. https://developer.apple.com/documentation/devicemanagement/caldav - Apple documents a profile signature as DER-encoded CMS Signed Data for integrity. The same signing identity is required when replacing a signed profile, except for MDM replacement. Apple's OTA guide says HTTPS protects the exchange and signing the profile does not add security to that exchange. The official docs do not define the exact Verified/Not Verified UI label for a TLS or Developer ID certificate. https://developer.apple.com/documentation/devicemanagement/configuring-multiple-devices-using-profiles https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/profile-service/profile-service.html - Apple’s user-facing install docs require opening Settings on iOS and opening the downloaded file on Mac; I found no documented URL scheme to jump directly to profile installation. Open security note: the current profile embeds the app password in an unencrypted plist. The one-use HTTPS download limits transport exposure, but signing alone does not satisfy Apple’s encrypted-profile guidance. The issue’s one-scan requirement asks for an embedded password, so I left the implementation in place and reported this trade-off for owner review.
Author
Owner

Finding: the first production E2E run timed out while it required the sticky phone title to be exactly “App password ready”. That title follows the heading currently at the sheet header, so “Account” is also a valid page title when the group heading has not reached the header. I narrowed the new capture assertion to the settings page titles and still reject the hidden DAVx⁵ content heading.

Finding: the first production E2E run timed out while it required the sticky phone title to be exactly “App password ready”. That title follows the heading currently at the sheet header, so “Account” is also a valid page title when the group heading has not reached the header. I narrowed the new capture assertion to the settings page titles and still reject the hidden DAVx⁵ content heading.
Author
Owner

I read the macOS 27 VM finding. The Mac section now has one step per line for Safari’s first-download Allow prompt, opening the downloaded profile, System Settings → General → Device Management → Downloaded, and installing it. It says the “author is unknown” confirmation is expected while the profile is unsigned. I removed the Mac password prompt step because the VM did not show one. The focused E2E now asserts this copy; I’m rebuilding the merged branch for the final captures and gates.

I read the macOS 27 VM finding. The Mac section now has one step per line for Safari’s first-download Allow prompt, opening the downloaded profile, System Settings → General → Device Management → Downloaded, and installing it. It says the “author is unknown” confirmation is expected while the profile is unsigned. I removed the Mac password prompt step because the VM did not show one. The focused E2E now asserts this copy; I’m rebuilding the merged branch for the final captures and gates.
Author
Owner

Finding: the first post-merge production E2E run failed because the old text locator for “The profile is unsigned” matched both the new install step and the warning note. I scoped the existing assertion to .unsigned-note; this only resolves the strict-mode ambiguity.

Finding: the first post-merge production E2E run failed because the old text locator for “The profile is unsigned” matched both the new install step and the warning note. I scoped the existing assertion to `.unsigned-note`; this only resolves the strict-mode ambiguity.
Author
Owner

Production screenshots from the local production build. Credential fields are masked. QR codes point to the E2E throwaway server, which has stopped; those links cannot be used.

390 px

390 light
390 dark

820 px

820 light
820 dark

1440 px

1440 light
1440 dark

Production screenshots from the local production build. Credential fields are masked. QR codes point to the E2E throwaway server, which has stopped; those links cannot be used. **390 px** ![390 light](https://git.kayg.org/attachments/05cafb6a-b942-43b3-916c-6d21c135ac3f) ![390 dark](https://git.kayg.org/attachments/132069ec-68c4-4167-a4e4-e51b491782cf) **820 px** ![820 light](https://git.kayg.org/attachments/9e7aefb8-5043-4dc3-bc65-3eaf2f18a72a) ![820 dark](https://git.kayg.org/attachments/f50b9237-3340-494b-9a28-3cd1eb7033ca) **1440 px** ![1440 light](https://git.kayg.org/attachments/5d1d532b-fe1f-4620-afab-a82973fd36bc) ![1440 dark](https://git.kayg.org/attachments/213d6ba1-44fa-4a3d-bbbd-7898c891e0a9)
Author
Owner

Completed

Fixed the credential overflow by wrapping the password and account ID chips, corrected the phone sheet title selection so hidden disclosure headings cannot replace the page title, and raised contrast on the disclosure and action controls. Updated the Mac instructions from the macOS 27 VM result: Safari’s first-download Allow prompt, System Settings → General → Device Management, and the expected unknown-author confirmation while the profile is unsigned. The Mac profile installed and synced Calendar end to end in the VM.

Captured production screenshots at 390, 820 and 1440 px in light and dark. All six are attached in the preceding comment; the QR URLs use the stopped E2E server and are not usable. Credentials are masked.

Files

  • apps/web/src/routes/settings/account/AppPasswordsGroup.svelte
  • apps/web/src/lib/components/OverlaySurface.svelte and OverlaySurface.svelte.test.ts
  • apps/web/e2e/app-passwords.mjs
  • apps/web/src/routes/settings/account/AccountSection.svelte, apps/web/e2e/calendar.mjs, apps/web/package.json, bun.lock
  • crates/calternal-auth/Cargo.toml, crates/calternal-auth/src/api.rs, crates/calternal-server/src/wire.rs, Cargo.lock
  • contracts/openapi.json, packages/api-client/src/generated.ts
  • tests/adversarial/attack2.py, tests/adversarial/setup.mjs

Merged dev once (ce35155a). Head: c834769d7102475378ca9a29954578de2297f19f. git push origin job/app-pw-setup reported Everything up-to-date; the worktree is clean.

Gates and verification

cargo fmt --check: exit 0, no output.

cargo clippy --all-targets -- -D warnings: exit 0.

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 19m 37s

cargo test: the completed log has 72 test result: summaries, all with 0 failed. The final workspace output was:

   Doc-tests calternal_tags

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

The outer zsh wrapper returned 1 after Cargo completed because it assigned to zsh’s read-only status variable (zsh:7: read-only variable: status). I checked all 72 summaries; none reported a failure. I did not rerun the suite.

bun run --cwd apps/web check:

Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/app-pw-setup/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run --cwd apps/web test:

 Test Files  112 passed (112)
      Tests  727 passed (727)
   Start at  16:27:20
   Duration  130.50s (transform 56%, environment 18%, import 14%, tests 9%, setup 4%)

Focused production E2E: app password e2e: QR, manual steps, Other apps, and Mac profile passed.

Targeted post-merge adversarial round:

finish 200
login web 200 installation 200
cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ]
invite 200
second user finish 200
fixture passkey login requests: 30/30
---------- app_profiles ----------
server alive at end: True

==== ROUND 2 FINDINGS 0

==== ROUND 2 SLOW 0

cargo clean:

     Removed 15890 files, 13.4GiB total

Web build output was removed.

Known gaps and decisions

The profile remains unsigned because there is no configured signing identity or supplied signing credential for VM verification. The macOS VM showed “Not Signed” and an unknown-author confirmation, then installed and synced successfully. The owner’s proposed TLS and Developer ID signing candidates still need separate configuration and macOS verification.

The profile contains the app password in an unencrypted plist. Apple’s CalDAV payload documentation says CalDAVPassword should only be used in encrypted profiles. The issue requires one-scan password autofill, so I retained the current flow and documented this confidentiality gap; signing alone would not encrypt the password.

The DESIGN doc does not specify sticky title ownership when a sheet contains hidden disclosures. I made explicit [data-sheet-title] headings take priority and ignored headings inside hidden content; the regression test covers the DAVx⁵ case. The screenshot build and the observed macOS 27 profile flow guided the short Mac instruction copy.

## Completed Fixed the credential overflow by wrapping the password and account ID chips, corrected the phone sheet title selection so hidden disclosure headings cannot replace the page title, and raised contrast on the disclosure and action controls. Updated the Mac instructions from the macOS 27 VM result: Safari’s first-download Allow prompt, System Settings → General → Device Management, and the expected unknown-author confirmation while the profile is unsigned. The Mac profile installed and synced Calendar end to end in the VM. Captured production screenshots at 390, 820 and 1440 px in light and dark. All six are attached in the preceding comment; the QR URLs use the stopped E2E server and are not usable. Credentials are masked. ## Files - `apps/web/src/routes/settings/account/AppPasswordsGroup.svelte` - `apps/web/src/lib/components/OverlaySurface.svelte` and `OverlaySurface.svelte.test.ts` - `apps/web/e2e/app-passwords.mjs` - `apps/web/src/routes/settings/account/AccountSection.svelte`, `apps/web/e2e/calendar.mjs`, `apps/web/package.json`, `bun.lock` - `crates/calternal-auth/Cargo.toml`, `crates/calternal-auth/src/api.rs`, `crates/calternal-server/src/wire.rs`, `Cargo.lock` - `contracts/openapi.json`, `packages/api-client/src/generated.ts` - `tests/adversarial/attack2.py`, `tests/adversarial/setup.mjs` Merged `dev` once (`ce35155a`). Head: `c834769d7102475378ca9a29954578de2297f19f`. `git push origin job/app-pw-setup` reported `Everything up-to-date`; the worktree is clean. ## Gates and verification `cargo fmt --check`: exit 0, no output. `cargo clippy --all-targets -- -D warnings`: exit 0. ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 19m 37s ``` `cargo test`: the completed log has 72 `test result:` summaries, all with `0 failed`. The final workspace output was: ```text Doc-tests calternal_tags running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` The outer zsh wrapper returned 1 after Cargo completed because it assigned to zsh’s read-only `status` variable (`zsh:7: read-only variable: status`). I checked all 72 summaries; none reported a failure. I did not rerun the suite. `bun run --cwd apps/web check`: ```text Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/app-pw-setup/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run --cwd apps/web test`: ```text Test Files 112 passed (112) Tests 727 passed (727) Start at 16:27:20 Duration 130.50s (transform 56%, environment 18%, import 14%, tests 9%, setup 4%) ``` Focused production E2E: `app password e2e: QR, manual steps, Other apps, and Mac profile passed`. Targeted post-merge adversarial round: ```text finish 200 login web 200 installation 200 cookies [ "__Host-calternal_session secure=true httpOnly=true sameSite=Lax" ] invite 200 second user finish 200 fixture passkey login requests: 30/30 ---------- app_profiles ---------- server alive at end: True ==== ROUND 2 FINDINGS 0 ==== ROUND 2 SLOW 0 ``` `cargo clean`: ```text Removed 15890 files, 13.4GiB total ``` Web build output was removed. ## Known gaps and decisions The profile remains unsigned because there is no configured signing identity or supplied signing credential for VM verification. The macOS VM showed “Not Signed” and an unknown-author confirmation, then installed and synced successfully. The owner’s proposed TLS and Developer ID signing candidates still need separate configuration and macOS verification. The profile contains the app password in an unencrypted plist. Apple’s CalDAV payload documentation says `CalDAVPassword` should only be used in encrypted profiles. The issue requires one-scan password autofill, so I retained the current flow and documented this confidentiality gap; signing alone would not encrypt the password. The DESIGN doc does not specify sticky title ownership when a sheet contains hidden disclosures. I made explicit `[data-sheet-title]` headings take priority and ignored headings inside hidden content; the regression test covers the DAVx⁵ case. The screenshot build and the observed macOS 27 profile flow guided the short Mac instruction copy.
kayg closed this issue 2026-09-28 14:44:23 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#318
No description provided.