# iCloud CalDAV Configuration ## Setup 1. Generate app-specific password at [appleid.apple.com](https://appleid.apple.com) → Security → App-Specific Passwords 2. Select "Mail" or "Calendar" service 3. Save the 16-char password (format: `xxxx-xxxx-xxxx-xxxx`) to `/root/.config/himalaya/g-germainebrown-icloud-calendar.pass` 4. Set file permissions: `chmod 600 /root/.config/himalaya/g-germainebrown-icloud-calendar.pass` ## Himalaya Config Add to `/root/.config/himalaya/config.toml`: ```toml [accounts.germaine-calendar] backend.type = "caldav" backend.host = "https://caldav.icloud.com" backend.login = "g@germainebrown.com" backend.auth.type = "password" backend.auth.cmd = "cat /root/.config/himalaya/g-germainebrown-icloud-calendar.pass" ``` ## Verification Test ```python import requests with open('/root/.config/himalaya/g-germainebrown-icloud-calendar.pass') as f: pw = f.read().strip() body = ''' ''' r = requests.request('PROPFIND', 'https://caldav.icloud.com/', auth=('g@germainebrown.com', pw), headers={'Content-Type': 'application/xml; charset=utf-8'}, data=body, timeout=15) # 207 = Multi-Status (auth OK). 401 = auth failed. ``` ## iCloud Calendar Home Discovery ```python body = ''' ''' r = requests.request('PROPFIND', 'https://caldav.icloud.com/1079451706/principal/', auth=('g@germainebrown.com', pw), headers={'Depth': '0'}, data=body, timeout=15) ``` Returns: `p{xx}-caldav.icloud.com:443/1079451706/calendars/` ## Pitfalls - Apple revokes old app-specific passwords when generating new ones. Always update the `.pass` file after regeneration. - The IMAP-style login test (`p02-imap.mail.me.com:993`) uses a different auth path than CalDAV. A failed IMAP login does NOT mean the password is wrong — use the PROPFIND test against `caldav.icloud.com` instead. - App-specific passwords expire when the Apple ID password is changed or the app is revoked. - Himalaya CalDAV (noted here) is experimental — the `calendar` subcommand may not exist. Use direct Python `requests` for CalDAV operations.