github rhoopr/kei v0.8.1

6 hours ago

Fixed

  • CloudKit 401 / persistent 421 no longer loops under Docker restart. Removed the 0.8.0 cache-fallback that accepted cached session data when /validate and /accountLogin both returned 421 — stale cached tokens would then produce a CloudKit 401, the process exited, and Docker restarted with the same cache forever. Auth now falls through to SRP in every stale-session case (trust_token is preserved, so 2FA is skipped in the common path). CloudKit 401 maps to ICloudError::SessionExpired and CloudKit 421 (after one pool reset) maps to ICloudError::MisdirectedRequest; the sync loop handles both by invalidating the validation cache, forcing SRP, and retrying init. A second failure surfaces cleanly. (#217, #199)
  • Final error output carries a timestamp. The top-level anyhow::Error is now routed through tracing::error! in addition to stderr, so crash messages in docker logs / journalctl carry the same YYYY-MM-DDTHH:MM:SS INFO kei::... prefix as the rest of the output. Makes the log timeline correlate cleanly instead of jumping from "kei::sync_loop: ..." lines to an unprefixed "Error: ..." line.
  • Duplicate --album names no longer error - sync --album X --album X previously failed with "Album 'X' not found" because the album map was drained on first match. Duplicate names are now deduplicated before resolution. (#219)

Added

  • Live download-integrity tests - end-to-end coverage for data-sacred invariants: deleted files get re-downloaded on next sync, and truncated files don't cause data loss. (#220)
  • Live gap tests - sync_watch_runs_multiple_cycles verifies watch mode completes 2+ cycles, sync_report_json_writes_valid_schema parses --report-json output against the schema, and new password subcommand edge cases cover clear-without-stored-credential and backend-with-empty-data-dir. (#219)

Changed

  • Simplified 421 recovery. init_photos_service no longer ladders through pool reset → 10/30/60s backoff → full re-auth (~150 lines). It resets the HTTP pool once and, on a second 421, surfaces the typed error through the same SRP re-auth path that handles 401. Per-endpoint pool-reset retries in validate_token / authenticate_with_token were hoisted to a single reset at auth::authenticate level, removing duplication.
  • Typed ICloudError::MisdirectedRequest. Replaces the Connection(String).contains("421") stringly-typed check in is_misdirected_request. check_apple_rscd no longer treats X-Apple-I-Rscd: 421 as an auth failure (never observed in the wild; only fed the removed cache-fallback).
  • CloudKit 421 response body logged at WARN. Issue #199's breakthrough was seeing Missing X-APPLE-WEBAUTH-USER cookie in the 421 body — the single most useful signal for distinguishing ADP-class 421 from session-class 421. Surfaced by default so the next reporter doesn't need RUST_LOG=debug.
  • Live tests portable across accounts - test setup reads ICLOUD_USERNAME, ICLOUD_PASSWORD, and KEI_TEST_ALBUM from env with sane defaults, no code edits needed. Shared bash helpers in tests/lib.sh; run-gap-tests.sh, run-deep-validation.sh, and run-docker-live.sh are now committed. (#222)
  • Consolidated 9 deprecation tests behind an assert_deprecated(args, should_succeed, hint) helper. Test names preserved so CI output still pinpoints the failing case. (#219)

Full changelog: CHANGELOG.md

Don't miss a new kei release

NewReleases is sending notifications on new releases.