···11+# CLAUDE.md
22+33+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44+55+## Project Overview
66+77+AtProtoBackup is a multi-platform (iOS, macOS) SwiftUI application for backing up AT Protocol (Bluesky) accounts. It downloads repository snapshots (CAR files) and associated blobs (images, videos, etc.) from AT Protocol Personal Data Servers (PDS), storing them in the user's Documents/backups/ directory for safekeeping and portability.
88+99+## Build Commands
1010+1111+This project uses `just` (justfile) for build automation. The Justfile is located at the project root.
1212+1313+### Common Commands
1414+1515+```bash
1616+# List all available commands
1717+just
1818+1919+# Build for iOS Simulator (iPhone 16 Pro, iOS 18.6)
2020+just build-ios-simulator
2121+2222+# Build for macOS
2323+just build-mac
2424+2525+# Build for all platforms
2626+just build-all
2727+2828+# Run tests on iOS Simulator
2929+just test-ios
3030+3131+# Run tests on macOS
3232+just test-mac
3333+3434+# Run all tests
3535+just test-all
3636+3737+# Clean build artifacts
3838+just clean
3939+4040+# Open project in Xcode
4141+just xcode
4242+```
4343+4444+### Direct xcodebuild Commands
4545+4646+If you need to customize build commands:
4747+4848+```bash
4949+# Build for iOS Simulator
5050+xcodebuild build \
5151+ -project AtProtoBackup.xcodeproj \
5252+ -scheme AtProtoBackup \
5353+ -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.6' \
5454+ -configuration Debug
5555+5656+# Build for macOS
5757+xcodebuild build \
5858+ -project AtProtoBackup.xcodeproj \
5959+ -scheme AtProtoBackup \
6060+ -destination 'platform=macOS' \
6161+ -configuration Debug
6262+6363+# Run tests
6464+xcodebuild test \
6565+ -project AtProtoBackup.xcodeproj \
6666+ -scheme AtProtoBackup \
6767+ -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.6'
6868+```
6969+7070+## Architecture Overview
7171+7272+### Data Flow
7373+7474+1. **Account Discovery**: Users add accounts by handle → `ATProtocolService` resolves DID → `Account` model created
7575+2. **Backup Discovery**: On launch, `BackupDiscovery` scans Documents/backups/ for existing backups and auto-adds them to the account list
7676+3. **Download Process**:
7777+ - `DownloadManager` orchestrates the backup
7878+ - `BlobDownloader` handles HTTP downloads using background URLSession
7979+ - Downloads CAR file first (repository snapshot)
8080+ - Lists all blob CIDs via `listBlobs` API
8181+ - Downloads blobs concurrently with background-safe URLSession
8282+ - `BackupMetadata` tracks completion and device info
8383+4. **Progress Tracking** (iOS only): Live Activities show real-time download progress via `BackgroundDownloadTracker`
8484+8585+### Core Components
8686+8787+#### Data Layer
8888+- **Account.swift**: SwiftData model for account storage (DID, handle, PDS URL)
8989+- **BackupMetadata.swift**: Codable struct tracking backup completion time, blob counts, device info, duration
9090+- **BackupDiscovery.swift**: Discovers existing backups in Documents/backups/{did}/ directories
9191+9292+#### Service Layer
9393+- **ATProtocolService.swift**: Resolves handles to DIDs via slingshot.microcosm.blue API
9494+- **BlobDownloader.swift**: Actor-based concurrent blob downloader using background URLSession for app suspension resilience. Implements retry logic, semaphore-based concurrency control, and skip-if-exists optimization.
9595+- **DownloadManager.swift**: Orchestrates full backup process (CAR download → list blobs → download blobs → save metadata). Manages Live Activities on iOS.
9696+9797+#### UI Layer
9898+- **ContentView.swift**: Main account list with master-detail navigation
9999+- **AccountDetailView.swift**: Shows account info and backup controls
100100+- **AccountListItemView.swift**: Individual account row in list
101101+- **DownloadSection.swift**: Download button and progress UI
102102+103103+#### Platform-Specific (iOS only)
104104+- **LiveActivityManager.swift**: Checks Live Activity permissions
105105+- **BackgroundDownloadTracker.swift**: MainActor class that batches Live Activity updates (every 10 blobs) to reduce overhead
106106+- **DownloadActivityAttributes.swift**: Defines Live Activity state and attributes
107107+- **AppDelegate.swift**: Handles background URL session events
108108+- **WidgetExtension/**: iOS Live Activity widget implementation
109109+110110+### Multi-Platform Considerations
111111+112112+The app uses `#if os(iOS)` preprocessor directives to conditionally compile iOS-specific features:
113113+- Live Activities and ActivityKit
114114+- UIKit-based device info (battery, device model)
115115+- Background download event handling via AppDelegate
116116+117117+macOS builds exclude these features but share the core download and backup logic.
118118+119119+### Dependencies
120120+121121+Swift Package Manager dependencies (in Xcode project):
122122+- **ATProtoKit**: AT Protocol Swift SDK for API interactions (repo syncing, blob listing)
123123+- **ZIPFoundation**: (Currently unused but available for future archive compression)
124124+125125+### File Storage Structure
126126+127127+```
128128+~/Documents/backups/
129129+└── {did}/
130130+ ├── backup-metadata.json # Backup completion info
131131+ ├── {handle}-{timestamp}.car # Repository snapshot
132132+ └── {cid}.{ext} # Individual blobs (images, videos, etc.)
133133+```
134134+135135+### Background Download Strategy
136136+137137+`BlobDownloader` uses `URLSession.background` with identifier `com.app.atproto.backup.downloader`. This allows:
138138+- Downloads continue when app is suspended/backgrounded
139139+- App wakes when downloads complete
140140+- Automatic reconnection to in-progress downloads after app restart
141141+142142+Progress updates are batched (every 10 blobs) to minimize main thread overhead and Live Activity update frequency.
143143+144144+### Error Handling
145145+146146+- **ATProtocolError**: API and network errors (invalidURL, invalidResponse, decodingError)
147147+- **BlobDownloadError**: HTTP errors, network failures, retry exhaustion
148148+- **BackupError**: File system errors (documentsDirectoryNotFound)
149149+- **GenericIntentError**: General error wrapper with custom messages
150150+151151+Blob downloads implement exponential backoff retry (up to 3 attempts by default) for transient network failures.
152152+153153+## Key Implementation Details
154154+155155+### Concurrency Model
156156+- `BlobDownloader` is an `actor` for thread-safe concurrent downloads
157157+- Uses async/await throughout for asynchronous operations
158158+- `BackgroundDownloadTracker` is `@MainActor` to safely update Live Activities
159159+- TaskGroup pattern for managing concurrent blob downloads
160160+161161+### SwiftData Usage
162162+- `Account` is a `@Model` for persistent storage
163163+- `modelContainer` configured with in-memory storage (Note: This means accounts don't persist between app launches!)
164164+- SwiftUI `@Query` used in ContentView for reactive account list
165165+166166+### Security-Scoped Resources
167167+- Uses bookmark data to maintain access to user-selected save locations
168168+- Properly calls `startAccessingSecurityScopedResource()` / `stopAccessingSecurityScopedResource()`
169169+170170+### Download Skipping
171171+- Before downloading each blob, checks if file with matching CID basename already exists
172172+- Returns `wasNewDownload: Bool` to track incremental vs full backups
173173+174174+## Testing Notes
175175+176176+- Unit tests: AtProtoBackupTests/
177177+- UI tests: AtProtoBackupUITests/
178178+- Tests currently minimal; expand coverage when modifying core download/backup logic