Skip to content

rhoopr/kei

Repository files navigation

kei logo

kei: photo sync engine

Built with Rust License: MIT Version Build
Downloads Homebrew Docker

Fast, parallel photo sync from the cloud to local storage. Single binary, runs unattended.

  • Parallel downloads - configurable concurrency, starts downloading before enumeration completes
  • Incremental sync - scans large libraries in seconds via CloudKit sync tokens, only fetches what changed
  • Resumable transfers - partial downloads resume via HTTP Range, verified by size and content hash
  • Single binary - no runtime dependencies, runs on macOS, Linux, and Windows
  • Unattended operation - watch mode, systemd integration, headless 2FA, Docker-ready

iCloud Photos is supported today. Google Takeout and Immich are next.

Tip

Coming from icloudpd? The Migration Guide maps every flag and shows how to pick up where you left off without re-downloading.


Install

Homebrew

brew install rhoopr/kei/kei

Docker

docker pull ghcr.io/rhoopr/kei:latest

See the Docker guide for compose files and headless 2FA.

Pre-built binaries

Grab one from GitHub Releases. macOS (Apple Silicon + Intel), Linux (ARM64 + x86_64), Windows (x86_64).

From source

git clone https://github.com/rhoopr/kei.git kei && cd kei
cargo build --release

Important

If you have Advanced Data Protection (ADP) enabled on your iCloud account, kei can't access your photos. ADP blocks the web API that kei uses. To fix this, you need to change both settings on your iPhone/iPad: disable ADP (Settings > Apple ID > iCloud > Advanced Data Protection) and enable "Access iCloud Data on the Web" (Settings > Apple ID > iCloud). See the Authentication wiki for details.

Quick start

kei sync -u you@example.com -d ~/Photos/iCloud --save-password

You'll be prompted for your password (or set ICLOUD_PASSWORD), then asked to approve 2FA on a trusted device. Downloads start right after. --save-password encrypts your password in the OS keyring (or an AES-256 file on headless systems), and kei saves your username and directory to ~/.config/kei/config.toml, so subsequent runs are just:

kei sync

Or use the interactive wizard: kei config setup.

For Docker, cron, and systemd setups, --password-file and --password-command give you more control over secret delivery. See the Credentials wiki page.

Usage

# Specific albums, skip videos, last 100 photos only
kei sync --album "Favorites" --recent 100 --skip-videos

# All libraries (personal + shared) in one run
kei sync --library all

# Keep syncing every hour
kei sync --watch-with-interval 3600

# Preview what would download
kei sync --only-print-filenames

# Dry run (no writes to disk)
kei sync --dry-run

Run kei sync --help for all flags, or see the wiki for the full CLI reference.

How it works

kei downloads on a streaming pipeline - it starts fetching files as soon as the first API page comes back, rather than waiting to enumerate the whole library. After the first full sync, it uses Apple's CloudKit syncToken to pull only what changed. A no-change check takes 1-2 API calls.

Downloads run with configurable concurrency (default 10). Partial downloads are saved as .kei-tmp files and resumed via HTTP Range headers. Every file is verified against its expected size and content-type before being committed.

State lives in a SQLite database alongside your session data (see --data-dir). The DB tracks what's been downloaded, what failed, and where files landed on disk.

Commands

Command
sync Download photos
login Authenticate and complete 2FA
list List albums or libraries
password Manage stored credentials (set, clear, backend)
config Show resolved config (show) or run the setup wizard (setup)
reset Delete state database (state) or clear sync tokens (sync-token)
status Show sync stats and database summary
verify Check downloads exist; --checksums for SHA256
import-existing Import local files so they aren't re-downloaded

Features

  • SQLite state tracking - never re-downloads what it already has
  • Watch mode with systemd notify, PID file, graceful shutdown
  • Multi-library sync (--library all for personal + shared)
  • Flexible password sources: prompt, env var, file, shell command, OS keyring
  • Content filtering: live photo mode, filename globs, album exclusions, date ranges, --recent N
  • Flexible folder structure with {album} token and full strftime support, EXIF datetime stamping
  • Multi-arch Docker images (amd64/arm64) with headless 2FA
  • Notification scripts on events (2FA required, sync complete, failures)
  • TOML config with env var overrides (KEI_*) for every flag
  • Structured exit codes (0 success, 2 partial, 3 auth) for scripting

Docs

Contributing

Contributions welcome. Open an issue first if you're planning something big.

cargo fmt -- --check && cargo clippy && cargo test

License

MIT - see LICENSE.md

Acknowledgments

kei started as icloudpd-rs, a Rust rewrite of icloud-photos-downloader. Thanks to the original maintainers for their reverse-engineering work.

About

Photo sync engine. Parallel downloads, incremental sync, state tracking, and unattended Docker operation, all in a single binary.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors

Languages