Keep using Photos.app like you always do. Attic quietly backs up your originals and edits to an S3 bucket you control. One-way, append-only.
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update docs for generic S3 config and new init command

+54 -23
+6 -5
CLAUDE.md
··· 1 1 # Attic 2 2 3 - Deno/TypeScript CLI for backing up iCloud Photos to Scaleway S3. Part of the photo-cloud system (companion: [ladder](https://github.com/tijs/ladder)). 3 + Deno/TypeScript CLI for backing up iCloud Photos to S3-compatible storage. Part of the photo-cloud system (companion: [ladder](https://github.com/tijs/ladder)). 4 4 5 5 ## Commands 6 6 7 7 ```bash 8 8 deno task check # Type check 9 - deno task test # Run tests (44 tests) 9 + deno task test # Run tests (54 tests) 10 10 deno task lint # Lint 11 11 deno task fmt # Format 12 12 deno task fmt:check # Check formatting ··· 16 16 17 17 ``` 18 18 shared/ # @attic/shared — PhotoAsset type, S3 path helpers 19 - cli/ # @attic/cli — commands, storage, manifest, export 20 - src/commands/ # scan, status, backup, verify, rebuild 21 - src/storage/ # S3 client + Keychain credential loading 19 + cli/ # @attic/cli — commands, config, storage, manifest, export 20 + src/commands/ # init, scan, status, backup, verify, rebuild 21 + src/config/ # Config file (load, validate, write) 22 + src/storage/ # Generic S3 client + Keychain credential loading 22 23 src/manifest/ # Local JSON manifest with atomic writes 23 24 src/export/ # Exporter interface + ladder subprocess integration 24 25 ```
+39 -15
README.md
··· 4 4 5 5 # Attic 6 6 7 - Back up your iCloud Photos library to Scaleway Object Storage (S3-compatible). 7 + Back up your iCloud Photos library to S3-compatible storage. 8 8 9 - Attic reads the Photos.sqlite database directly, exports originals via a companion Swift tool called [ladder](https://github.com/tijs/ladder), and uploads them to a Scaleway S3 bucket. A local manifest tracks what has already been backed up so subsequent runs only upload new assets. 9 + Attic reads the Photos.sqlite database directly, exports originals via a companion Swift tool called [ladder](https://github.com/tijs/ladder), and uploads them to an S3-compatible bucket. A local manifest tracks what has already been backed up so subsequent runs only upload new assets. 10 + 11 + Works with any S3-compatible provider. EU-friendly options include [Scaleway](https://www.scaleway.com/en/object-storage/), [Hetzner](https://www.hetzner.com/storage/object-storage), and [OVH](https://www.ovhcloud.com/en/public-cloud/object-storage/). 10 12 11 13 ## Prerequisites 12 14 13 15 - [Deno](https://deno.land/) (v2+) 14 16 - The [ladder](https://github.com/tijs/ladder) binary. Ladder is a separate Swift tool that uses PhotoKit to export original photo/video files from the Photos library. 15 - - A Scaleway Object Storage bucket and API credentials 17 + - An S3-compatible storage bucket and API credentials 16 18 - macOS (Photos.sqlite access and Keychain are macOS-only) 17 19 18 20 ## Setup 19 21 20 - Store your Scaleway S3 credentials in the macOS Keychain: 22 + Run the interactive setup: 21 23 22 24 ```bash 23 - security add-generic-password -s attic-s3-access-key -a attic -w "<your-access-key>" 24 - security add-generic-password -s attic-s3-secret-key -a attic -w "<your-secret-key>" 25 + deno task init 25 26 ``` 26 27 28 + This prompts for your S3 endpoint, region, bucket name, and credentials. Config is saved to `~/.attic/config.json` and credentials are stored in the macOS Keychain. 29 + 27 30 Build the ladder binary (see [ladder](https://github.com/tijs/ladder) for details): 28 31 29 32 ```bash ··· 36 39 37 40 All commands are run via `deno task`: 38 41 39 - ### scan 42 + ### init 40 43 41 - Scan the Photos library and print statistics (asset counts, sizes, types, local vs iCloud-only). 44 + Interactive setup — configure S3 connection and store credentials. 42 45 43 46 ```bash 44 - deno task scan 47 + deno task init 45 48 ``` 46 49 47 - Optionally pass a custom database path: 50 + ### scan 51 + 52 + Scan the Photos library and print statistics (asset counts, sizes, types, local vs iCloud-only). 48 53 49 54 ```bash 50 - deno task scan /path/to/Photos.sqlite 55 + deno task scan 51 56 ``` 52 57 53 58 ### status ··· 66 71 deno task backup 67 72 ``` 68 73 69 - Flags (append after `--`): 70 - 71 74 | Flag | Description | 72 75 |---|---| 73 76 | `--dry-run` | Show what would be uploaded without uploading | 74 77 | `--limit N` | Back up at most N assets | 75 78 | `--batch-size N` | Assets per ladder export batch (default: 50) | 76 79 | `--type photo\|video` | Only back up photos or videos | 77 - | `--bucket NAME` | S3 bucket name (default: `photo-cloud-storage`) | 80 + | `--bucket NAME` | Override bucket from config | 78 81 | `--ladder PATH` | Path to the ladder binary (or set `LADDER_PATH` env var) | 79 82 | `--db PATH` | Path to Photos.sqlite | 80 83 ··· 90 93 |---|---| 91 94 | `--deep` | Download each object and re-verify SHA-256 checksum (slow) | 92 95 | `--rebuild-manifest` | Reconstruct the local manifest from S3 metadata files | 93 - | `--bucket NAME` | S3 bucket name (default: `photo-cloud-storage`) | 96 + | `--bucket NAME` | Override bucket from config | 97 + 98 + ## Configuration 99 + 100 + Attic stores its configuration at `~/.attic/config.json`: 101 + 102 + ```json 103 + { 104 + "endpoint": "https://s3.fr-par.scw.cloud", 105 + "region": "fr-par", 106 + "bucket": "my-photo-backup", 107 + "pathStyle": true, 108 + "keychain": { 109 + "accessKeyService": "attic-s3-access-key", 110 + "secretKeyService": "attic-s3-secret-key" 111 + } 112 + } 113 + ``` 114 + 115 + The `keychain` section is optional and defaults to the service names shown above. Credentials are always stored in the macOS Keychain, never in config files or environment variables. 116 + 117 + `scan` and `status` work without config (they only read Photos.sqlite). `backup` and `verify` require config and will tell you to run `attic init` if it's missing. 94 118 95 119 ## Testing 96 120
+8 -2
docs/architecture.md
··· 93 93 94 94 Both modes use a bounded concurrency pool (default 50 workers). Errors are capped at 1,000 to prevent unbounded memory growth. 95 95 96 + ## Configuration 97 + 98 + Attic reads its configuration from `~/.attic/config.json`. The config file specifies the S3 endpoint, region, bucket, path-style preference, and Keychain service names. It's created by `attic init` or manually. 99 + 100 + `scan` and `status` work without config (they only read Photos.sqlite). `backup` and `verify` require config and fail fast with a clear message if it's missing or invalid. 101 + 96 102 ## Credentials 97 103 98 - S3 credentials are stored in the macOS Keychain under service names `attic-s3-access-key` and `attic-s3-secret-key`. They are read at runtime via `security find-generic-password` — never stored in env vars, config files, or code. 104 + S3 credentials are stored in the macOS Keychain under configurable service names (defaults: `attic-s3-access-key` and `attic-s3-secret-key`). They are read at runtime via `security find-generic-password` — never stored in env vars, config files, or code. 99 105 100 106 ## Interfaces and testability 101 107 ··· 103 109 104 110 | Interface | Real implementation | Mock | 105 111 |-----------|-------------------|------| 106 - | `S3Provider` | AWS SDK client for Scaleway | In-memory `Map<string, Uint8Array>` | 112 + | `S3Provider` | AWS SDK client for any S3-compatible endpoint | In-memory `Map<string, Uint8Array>` | 107 113 | `Exporter` | Ladder subprocess | Returns pre-configured assets from a `Map` | 108 114 | `ManifestStore` | File-based JSON with atomic writes | Same implementation, pointed at a temp dir | 109 115 | `PhotosDbReader` | SQLite reader for Photos.sqlite | In-memory SQLite with test fixtures |
+1 -1
docs/plans/2026-03-13-feat-ux-open-source-readiness-plan.md
··· 1 1 --- 2 2 title: "feat: UX and Open-Source Readiness" 3 3 type: feat 4 - status: active 4 + status: completed 5 5 date: 2026-03-13 6 6 --- 7 7