···1414 │
1515 ├─→ S3 upload (original + metadata JSON)
1616 │
1717- └─→ manifest.json (local progress tracker)
1717+ └─→ manifest.json (on S3, shared across machines)
1818```
19192020Attic never modifies Photos.sqlite. The database is opened read-only.
···106106107107### 4. Manifest
108108109109-The manifest is a JSON file at `~/.attic/manifest.json` mapping UUID to
110110-`{ s3Key, checksum, backedUpAt }`. It's saved periodically during backup (every
111111-50 assets by default) and always at the end. Writes are atomic: write to `.tmp`,
112112-then rename.
109109+The manifest is stored on S3 at `manifest.json` in the bucket root, mapping UUID
110110+to `{ s3Key, checksum, backedUpAt }`. S3 is the single source of truth — there
111111+is no local manifest file. This enables cross-machine and cross-app (CLI ↔ menu
112112+bar app) continuity.
113113+114114+On backup start, the manifest is downloaded from S3. It's saved back to S3
115115+periodically (every 50 assets by default) for crash resilience, and always at
116116+the end of a run.
117117+118118+**Migration**: existing local manifests at `~/.attic/manifest.json` are
119119+automatically uploaded to S3 on first run via `loadManifestWithMigration()`.
113120114121The manifest can be reconstructed from S3 via `verify --rebuild-manifest`, which
115122reads every `metadata/assets/*.json` file and validates UUID format, S3 key
···132139specifies the S3 endpoint, region, bucket, path-style preference, and Keychain
133140service names. It's created by `attic init` or manually.
134141135135-`scan` and `status` work without config (they only read Photos.sqlite). `backup`
136136-and `verify` require config and fail fast with a clear message if it's missing
137137-or invalid.
142142+`scan` works without config (it only reads Photos.sqlite). All other commands
143143+(`status`, `backup`, `verify`, `refresh-metadata`) require config and S3
144144+credentials since the manifest is stored on S3.
138145139146## Credentials
140147···151158| ---------------- | --------------------------------------------- | ------------------------------------------ |
152159| `S3Provider` | AWS SDK client for any S3-compatible endpoint | In-memory `Map<string, Uint8Array>` |
153160| `Exporter` | Ladder subprocess | Returns pre-configured assets from a `Map` |
154154-| `ManifestStore` | File-based JSON with atomic writes | Same implementation, pointed at a temp dir |
161161+| `ManifestStore` | S3-backed JSON (`manifest.json` in bucket) | Same S3 mock used for uploads |
155162| `PhotosDbReader` | SQLite reader for Photos.sqlite | In-memory SQLite with test fixtures |
156163157164Tests never hit external services, credentials, or the real Photos library.