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.

Use dynamic imports so each command only loads its dependencies

verify no longer loads SQLite (--allow-ffi not needed), scan/status
no longer load the AWS SDK. Each command branch imports only what it uses.

+28 -17
+27 -16
cli/mod.ts
··· 1 - import { openPhotosDb } from "./src/photos-db/reader.ts"; 2 - import { printScanReport } from "./src/commands/scan.ts"; 3 - import { printStatusReport } from "./src/commands/status.ts"; 4 - import { runBackup } from "./src/commands/backup.ts"; 5 - import { runVerify } from "./src/commands/verify.ts"; 6 - import { rebuildManifest } from "./src/commands/rebuild.ts"; 7 - import { createManifestStore } from "./src/manifest/manifest.ts"; 8 - import { 9 - createS3Provider, 10 - loadKeychainCredentials, 11 - } from "./src/storage/s3-client.ts"; 12 - import { createLadderExporter } from "./src/export/exporter.ts"; 1 + const DEFAULT_BUCKET = "photo-cloud-storage"; 13 2 14 3 const command = Deno.args[0]; 15 4 16 5 switch (command) { 17 6 case "scan": { 7 + const { openPhotosDb } = await import("./src/photos-db/reader.ts"); 8 + const { printScanReport } = await import("./src/commands/scan.ts"); 9 + 18 10 const dbPath = Deno.args[1]; // optional override 19 11 const reader = openPhotosDb(dbPath); 20 12 try { ··· 26 18 break; 27 19 } 28 20 case "status": { 21 + const { openPhotosDb } = await import("./src/photos-db/reader.ts"); 22 + const { printStatusReport } = await import("./src/commands/status.ts"); 23 + const { createManifestStore } = await import("./src/manifest/manifest.ts"); 24 + 29 25 const dbPath = Deno.args[1]; // optional override 30 26 const reader = openPhotosDb(dbPath); 31 27 try { ··· 38 34 break; 39 35 } 40 36 case "backup": { 37 + const { openPhotosDb } = await import("./src/photos-db/reader.ts"); 38 + const { runBackup } = await import("./src/commands/backup.ts"); 39 + const { createManifestStore } = await import("./src/manifest/manifest.ts"); 40 + const { createS3Provider, loadKeychainCredentials } = await import( 41 + "./src/storage/s3-client.ts" 42 + ); 43 + const { createLadderExporter } = await import("./src/export/exporter.ts"); 44 + 41 45 const flags = parseBackupFlags(Deno.args.slice(1)); 42 46 const reader = openPhotosDb(flags.dbPath); 43 47 try { ··· 48 52 const credentials = await loadKeychainCredentials(); 49 53 const s3 = createS3Provider( 50 54 credentials, 51 - flags.bucket ?? "photo-cloud-storage", 55 + flags.bucket ?? DEFAULT_BUCKET, 52 56 ); 53 57 54 58 const ladderPath = flags.ladderPath ?? ··· 67 71 break; 68 72 } 69 73 case "verify": { 74 + const { runVerify } = await import("./src/commands/verify.ts"); 75 + const { rebuildManifest } = await import("./src/commands/rebuild.ts"); 76 + const { createManifestStore } = await import("./src/manifest/manifest.ts"); 77 + const { createS3Provider, loadKeychainCredentials } = await import( 78 + "./src/storage/s3-client.ts" 79 + ); 80 + 70 81 const verifyFlags = parseVerifyFlags(Deno.args.slice(1)); 71 82 72 83 const credentials = await loadKeychainCredentials(); 73 84 const s3 = createS3Provider( 74 85 credentials, 75 - verifyFlags.bucket ?? "photo-cloud-storage", 86 + verifyFlags.bucket ?? DEFAULT_BUCKET, 76 87 ); 77 88 const manifestStore = createManifestStore(); 78 89 ··· 99 110 console.log(` --batch-size N Assets per ladder batch (default: 50)`); 100 111 console.log(` --type photo|video Only back up photos or videos`); 101 112 console.log( 102 - ` --bucket NAME S3 bucket (default: photo-cloud-storage)`, 113 + ` --bucket NAME S3 bucket (default: ${DEFAULT_BUCKET})`, 103 114 ); 104 115 console.log(` --ladder PATH Path to ladder binary`); 105 116 console.log(` --db PATH Path to Photos.sqlite`); ··· 107 118 console.log(` --deep Download and re-checksum each object`); 108 119 console.log(` --rebuild-manifest Reconstruct manifest from S3 metadata`); 109 120 console.log( 110 - ` --bucket NAME S3 bucket (default: photo-cloud-storage)`, 121 + ` --bucket NAME S3 bucket (default: ${DEFAULT_BUCKET})`, 111 122 ); 112 123 console.log(`\nUsage: deno task <command>`); 113 124 if (command) {
+1 -1
deno.json
··· 9 9 "scan": "deno run --allow-read --allow-env --allow-ffi cli/mod.ts scan", 10 10 "status": "deno run --allow-read --allow-write --allow-env --allow-ffi cli/mod.ts status", 11 11 "backup": "deno run --allow-read --allow-write --allow-env --allow-sys --allow-ffi --allow-net=s3.fr-par.scw.cloud --allow-run cli/mod.ts backup", 12 - "verify": "deno run --allow-read --allow-write --allow-env --allow-sys --allow-ffi --allow-net=s3.fr-par.scw.cloud --allow-run=security cli/mod.ts verify" 12 + "verify": "deno run --allow-read --allow-write --allow-env --allow-sys --allow-net=s3.fr-par.scw.cloud --allow-run=security cli/mod.ts verify" 13 13 } 14 14 }