Docs/Lanes Link/Providers/iCloud

iCloud

Mail, Calendar, and Contacts from one Apple Account, plus your iCloud Drive folder

console
$ lanes link connect icloud --profile personal --workspace local

That connects Mail, Calendar, and Contacts together. You are asked for your Apple Account address and one app-specific password, once, because Apple issues app-specific passwords at account scope.

iCloud Drive is separate and needs no credential at all. See iCloud Drive works differently below.

What your agent can do

iCloud Maillist_mailboxes, search_messages, get_message. Write: mark_messages, move_messages, send_message
iCloud Calendarlist_calendars, list_events, get_event. Write: create_event, update_event, delete_event
iCloud Contactslist_addressbooks, search_contacts. Write: create_contact
iCloud Drivelist_files, search_files, read_file, file_info. Write: write_file, move_file, create_folder, trash_file

These are three separate providers, which is what lets you cut one without touching the others. connect grants each whole provider, writes included, so sending mail and creating events work immediately and narrowing is something you do rather than something you undo:

console
$ lanes link policy deny icloud_mail.send_message --profile personal --workspace local   # read mail, never send it
$ lanes link policy deny 'icloud_mail.*' --profile personal --workspace local            # calendars and contacts still work

Reading never marks mail as read. Every read path opens the mailbox with EXAMINE and fetches with BODY.PEEK, so an agent reading your inbox does not change what you see in Mail. Marking something read is a separate, write-bundle capability.

Nothing here can delete mail. There is no EXPUNGE, and \Deleted is not a flag an agent can set. Moving a message to Trash is available and reversible; permanent deletion is not offered.

Editing an event does not disturb the rest of it. update_event patches what is there rather than rebuilding it, so attendees and their replies, alarms, repetition rules, and anything another client wrote all survive a change of time or title. Both update_event and delete_event are conditional on the version they read, so if you edited the event on your phone in between, the write is refused rather than overwriting quietly.

Editing a contact is not offered. vCard round-trips are where fields get destroyed, silently and permanently. Creating and searching carry no such risk.

The app-specific password

Your normal Apple Account password will not work. Apple refuses it for third-party clients, and there is no way around that.

  1. Sign in at account.apple.com and open Sign-In and Security.
  2. Two-factor authentication must be on. Without it Apple does not offer app-specific passwords at all, and the section below simply does not appear.
  3. App-Specific Passwords → Generate. Name it Lanes Link, because the name is the only way to revoke this one later without cutting off your other devices.
  4. Copy the sixteen characters. Apple shows them once, formatted xxxx-xxxx-xxxx-xxxx. The hyphens are cosmetic and either form is accepted.

You can hold 25 at a time, and revoke them individually.

Changing your Apple Account password revokes every app-specific password at once. This is the cause of most sudden iCloud failures: everything works for weeks and then all three services stop together. Generate a new password and run lanes link connect icloud --replace, which re-prompts and updates all three. Without --replace, connect finds the revoked password already stored and reuses it.

The password goes into the encrypted credential store, never into config. It is stored once at icloud/<account> and shared by the three providers.

iCloud Drive works differently

console
$ lanes link connect icloud_drive --profile personal --workspace local

No password, no browser, nothing to type. Apple publishes no protocol for Drive at all, but on a Mac it is a folder the system keeps in sync, so this reads it directly.

That has one consequence worth understanding: it only works on the Mac holding the files. There is no credential involved, so there is nothing that could be copied to a server elsewhere. The permission is macOS's, held against the process on that machine.

  • Nothing deletes permanently. trash_file moves to the Finder's Trash.
  • Everything stays inside the folder. Paths are resolved through symlinks before being checked, so a link pointing out of iCloud Drive is refused rather than followed. .git, .ssh, and node_modules are never reachable.
  • If "Optimise Mac Storage" has evicted a file, reading it says so and tells you how to fetch it, rather than returning the placeholder's contents as if they were the file.

You may need to grant your terminal Files and Folders access in System Settings, Privacy and Security, the first time.

What is not available

  • Reminders and Notes. Apple moved to-do lists to a private store after iOS 13, so CalDAV returns legacy data or empty tombstones, and Notes were never exposed over any open protocol. Neither is reachable by any third-party client, not just this one.
  • Attachment contents, when reading. get_message reports each attachment's name, type, and size, but not its bytes. Sending attachments does work, and forwarding one that arrived by mail is done by naming it ({ "message_id": "<...>" }), which never materialises the bytes.
  • A display name, unless you set one. SMTP sends exactly what is composed, so without config.from_name on the connection the From header is a bare address.

Limits worth knowing

  • Event queries are capped at one year per request; iCloud rejects wider windows.
  • There is no push. Nothing here subscribes; an agent asks when it wants to know.
  • iCloud throttles reconnection harder than open sessions, so the mail connector holds one connection per account and reuses it.

Is there an OAuth option?

Apple shipped one in October 2025, and Outlook uses it. It is partner-gated: the developer service behind it publishes no scopes for these services, so it is not available to write against. If that changes it is a small change here, because auth is orthogonal to connectivity: the three manifests would swap auth: basic for auth: oauth and nothing about the connectors would move.

Troubleshooting

What you seeWhat it is
The server rejected the credentialAn Apple Account password used where an app-specific one belongs, or a password revoked by an account password change. Generate a new one and run connect icloud --replace
A first connect with a mistyped passwordFiled under icloud/pending and re-prompted automatically, so it needs no flag
Two accountsConnect each in turn. A second connect icloud_mail asks which account, or pass --id to name one
Authentication fails with a local part as the usernameGive the full address. It works for both IMAP and SMTP, and SMTP requires it

The --replace on the first row matters: the credential that was refused is the one in the store, so a bare re-run reuses it rather than asking. Nothing is discarded until you have entered the new password in full, so cancelling at the prompt leaves the old one alone.


Next: Add it to your agent. For what the audit log keeps and withholds, see the capability reference.