ADR 0030: iCloud IMAP credential connect
Status
Accepted
Context
ADR 0027 made iCloud a read-only IMAP inventory adapter and rejected write methods. Sprint 10b needs a product path that connects Apple infrastructure (jskoch67@icloud.com) and legacy Apple (Jason.s.koch@icloud.com) without OAuth. Apple issues an app-specific password for IMAP. That secret must not be stored in git, Jira, or logs.
Decision
/accountsposts the address and app-specific password to/api/providers/icloud/connectover the existing session and CSRF check.- The server speaks IMAP to
imap.mail.me.com:993, requires a successful login and a non-empty mailbox list, then upsertsconnected_accountswith providericloudand a read-only capability profile. - The password is stored only as
provider_tokens.token_type = imap_secretciphertext underTOKEN_ENCRYPTION_KEY. It is not an OAuth access token and is not returned on the health API. - One user may have multiple iCloud rows. Identity is the lowercased address. The UI labels every iCloud row read-only and does not render archive, move, or delete controls.
- Write methods on the adapter stay
CAPABILITY_NOT_SUPPORTED.
Alternatives
- Reuse
token_type = accessfor the IMAP password — rejected; operators would treat it as a bearer token. - iCloud OAuth — rejected; Apple mail connect for this product is IMAP.
- Enable adapter writes now and hide them only in CSS — rejected; ADR 0027 already denies the capability.
Consequences
Migration 0021_icloud_imap_secret.sql extends the token type check. ADR 0031 later makes iCloud read-write for move. Live login remains owner-gated because the app-specific password is not an environment variable. Gmail OAuth and Microsoft Graph connect are unchanged. ADR 0032 replaces the read-only label and the denied iCloud write methods: archive, mark read, flag, delete, and move run, and a second iCloud connect keeps the first row.
