This started with a question that kept me up at night. A user's Mac backs up to a Synology over SMB, and the network drops sometimes. While teaching Foldic to survive that gracefully, I asked myself: when a scan decides a photo is already backed up, what does it actually compare? The answer: file names. Only file names.
Names only, on purpose
Foldic's scan computes the file names every photo in an album should have, and compares them against what is in the folder. The names carry each photo's identity, so renames and naming-scheme changes are recognised — but size is never checked, content is never checked, dates are never checked. That is a deliberate trade: checking content means reading the entire drive on every scan, and a photo whose original lives only in iCloud would have to be downloaded before there is anything to compare. Scans would go from seconds to hours.
Foldic's own end is covered. Every file is written into a staging folder first and moved to its real name only when complete — and on a local file system like APFS that move is atomic: the file either appears whole or does not exist at all, with no state in between. So a cancel, a crash, a yanked cable never leaves half a photo pretending to be a whole one.
The trouble is the end we do not control. The guarantee has a premise: the file system does what it says. A NAS can tell you "written" and then lose data that never durably landed when the connection drops — on a network drive, even "the move is atomic" does not fully hold (see the appendix). A drive unplugged without ejecting can keep a file whose name is right and whose tail is zeros. And in every scan after that, those files count as backed up.
The failure that looks like success
The worst failure for a backup tool is not an error message. Errors you can see; errors you deal with. The worst failure looks like success: a green check, "12,430 photos backed up" — and on the day you actually need to restore, one of them opens as half a photo and a grey rectangle.
That day might be years away. Whether the source still exists by then, nobody can promise.
This is what I was afraid of. We believe the sync finished; what we actually have is broken files.
Originals never change, so the expensive step happens once
To prove a backup is good, sooner or later you have to read the bytes and compare them. How expensive that is depends on what you compare them against.
Here we have one built-in advantage: originals in the Photos library are immutable. You can edit, crop, filter — the original stays the file your camera made. So once a file's bytes on disk have been checked against the bytes the library serves, and the hash written down, every later verification only needs to read the disk side. The expensive comparison happens once per file, ever.
The whole feature is designed around that one fact.
Three lines of defense
Record at write time. From the next version on, whenever a sync writes a file, Foldic computes its SHA-256 and size and records them in an index at the root of the backup drive. The index travels with the drive — plug it into another Mac and the evidence comes along. New files are born fully accounted for; you do nothing.
Read it back. A new setting, on by default: after each sync, every file just written is read back from the backup disk and compared against what was sent. This is the check aimed squarely at "the NAS said it was written, and it was not" — it catches the mistake at the only moment it is still free to fix, instead of years later. A mismatched file goes to the Trash and the next scan rewrites it.
Deep Compare. Right-click an album, or run it for all albums from Settings. Files with a recorded hash are verified by reading the disk alone — the library is never touched. Files without one (backups that predate this version) are checked against the bytes the library serves, and the result is recorded — so the expensive pass happens once per file, ever. A photo whose original lives only in iCloud while downloads are off is honestly marked unverifiable. A backup tool has no business being optimistic on your behalf.
Damaged files are named right on the album's row, next to a button: re-back up the damaged files. The bad copy goes to the Trash, a fresh export replaces it, the evidence is renewed. Because the source is immutable, the repair is always lossless.
The afternoon I broke my own backup
With the feature built, I ran an experiment.
I let Foldic sync 331 files to a test drive and ran the first Deep Compare. Those 331 files were "old backups" — no records in the index — so all of them were verified against the library: 331 passed, and the index was written.
Then I picked one 3.3 MB photo and flipped its 1,000,000th byte. The file size did not change by a single byte. The date did not change. The name did not change. To the old Foldic, this photo would have counted as backed up forever.
I ran Deep Compare again. This time the index held 331 records, so the whole pass read only the disk and finished in six seconds — and it named the file:
DAMAGED 15846A2E-…jpg: recorded 3315306B 06723ae2…, disk has 3315306B c87e7a45…
The recorded hash and the on-disk hash matched exactly what I computed in a terminal, out of band. One click on repair, the photo was re-exported, and a third pass reported: 396 files, all verified, zero damaged.
(The experiment also caught one of my own bugs along the way — the index could fail to save under a sandbox path. That is what tests like this are for: they verify the feature, and they verify the person who wrote it.)
A backup's worth is decided on the day you restore
"Sync complete" is a promise. This feature is the audit — and the audit's rules are: verify new files on the spot, re-verify old files on a schedule you choose, and say unverifiable out loud when that is the truth.
A backup's worth was never decided on the day it was written. It is decided on the day you restore. We want every photo you open that day to be whole.
Appendix: where "atomic" ends
To be precise about the boundary. On local file systems (APFS, HFS+), rename is an atomic system call — that is the basis for "never half a photo". SMB has atomic-rename semantics at the protocol level too, but that describes the server's file system. On the actual write path there are write-back caches on both the client and the server: when your Mac hears "written", the data may still be in memory on either end. If the connection dies in that window, the protocol promises nothing about what is on the disk. That is why "read it back" runs at the end of a sync instead of trusting anyone's acknowledgement — the bytes read back are the only evidence that no cache and no protocol vouched for.
(Strictly, even a read-back can be served from the client's cache. Which is why Deep Compare is the final line: it can run at any time, after a reboot, even from a different Mac.)
When can you use this? Write-time recording, read-back verification and Deep Compare shipped with version 0.8, available now. The index is evidence, not authority: deleting it breaks nothing — the next Deep Compare simply has to verify against the library again.
시작은 밤잠을 설치게 한 질문 하나였습니다. 어느 사용자의 Mac은 SMB로 Synology에 백업하는데, 네트워크가 가끔 끊깁니다. 끊김을 우아하게 견디는 처리를 만들다가 스스로에게 물었습니다 — 스캔이 "이 사진은 백업됨"이라고 판정할 때, 실제로 무엇을 비교하는가? 답: 파일 이름. 파일 이름뿐.
이름만 보는 것은 의도된 설계
Foldic의 스캔은 앨범의 각 사진이 가져야 할 파일 이름을 계산해서 폴더 안의 실제 이름과 대조합니다. 이름에는 사진의 식별자가 들어 있어서 이름 변경이나 명명 방식 전환은 알아봅니다 — 하지만 크기는 안 보고, 내용도 안 보고, 날짜도 안 봅니다. 의도된 트레이드오프입니다. 내용을 비교하려면 스캔할 때마다 드라이브 전체를 읽어야 하고, 원본이 iCloud에만 있는 사진은 비교할 대상을 얻기 위해 먼저 내려받아야 합니다. 스캔이 몇 초에서 몇 시간이 됩니다.
Foldic 쪽 끝은 지켜지고 있습니다. 모든 파일은 먼저 스테이징 폴더에 쓰이고, 완성된 뒤에야 제 이름으로 옮겨집니다 — APFS 같은 로컬 파일 시스템에서 이 이동은 원자적입니다. 파일은 온전히 나타나거나 아예 존재하지 않거나, 중간 상태가 없습니다. 그래서 취소도, 크래시도, 케이블을 뽑아도, 반쪽짜리 사진이 온전한 척 남는 일은 없습니다.
문제는 우리가 통제하지 못하는 쪽 끝입니다. 이 보장에는 전제가 있습니다: 파일 시스템이 말한 대로 한다는 것. NAS는 "썼습니다"라고 답한 뒤, 연결이 끊길 때 아직 실제로 안착하지 않은 데이터를 잃을 수 있습니다 — 네트워크 드라이브에서는 "이동은 원자적"이라는 전제조차 완전히 성립하지 않습니다(글 끝의 부록 참고). 꺼내지 않고 뽑힌 드라이브는 이름은 맞는데 끝이 0으로 채워진 파일을 남길 수 있습니다. 그리고 그 파일들은 이후의 모든 스캔에서 "백업됨"으로 집계됩니다.
성공처럼 보이는 실패
백업 도구의 최악의 실패는 오류 메시지가 아닙니다. 오류는 보이고, 대처할 수 있습니다. 최악의 실패는 성공처럼 보이는 것입니다: 초록색 체크, "12,430장 백업됨" — 그리고 정말 복원이 필요한 날, 그중 한 장이 반쪽과 회색 사각형으로 열립니다.
그날은 몇 년 뒤일지도 모릅니다. 그때 원본이 아직 존재할지는 아무도 약속할 수 없습니다.
제가 두려워한 것이 이것입니다. 동기화가 끝났다고 믿고 있지만, 실제로 가진 것은 깨진 파일들.
원본은 불변 — 그래서 비싼 일은 평생 한 번
백업이 온전하다는 것을 증명하려면 결국 바이트를 읽어서 비교해야 합니다. 그 비용이 얼마나 드는지는 무엇과 비교하느냐에 달려 있습니다.
여기에는 타고난 이점이 하나 있습니다: '사진' 보관함의 원본은 불변입니다. 편집하고, 자르고, 필터를 입혀도 원본은 카메라가 만든 그 파일 그대로입니다. 그래서 어떤 파일의 디스크 바이트와 보관함이 내어주는 바이트를 한 번 대조해서 해시를 기록해 두면, 이후의 모든 검증은 디스크 쪽만 읽으면 됩니다. 비싼 대조는 파일마다 평생 한 번만 일어납니다.
기능 전체가 이 하나의 사실을 축으로 설계되었습니다.
세 겹의 방어선
쓸 때 기록한다. 다음 버전부터, 동기화가 파일을 쓸 때마다 Foldic은 그 파일의 SHA-256과 크기를 계산해 백업 드라이브 루트의 색인에 기록합니다. 색인은 드라이브와 함께 이동합니다 — 다른 Mac에 꽂으면 증거도 함께 갑니다. 새 파일은 태어날 때부터 기록 완료. 당신은 아무것도 하지 않아도 됩니다.
다시 읽어 비교한다. 기본으로 켜져 있는 새 설정: 동기화가 끝날 때마다, 방금 쓴 파일을 백업 디스크에서 다시 읽어 보낸 내용과 대조합니다. "NAS는 썼다고 했지만 쓰지 않았다"를 정면으로 겨눈 한 수입니다 — 몇 년 뒤가 아니라, 아직 공짜로 고칠 수 있는 유일한 순간에 잡아냅니다. 불일치한 파일은 휴지통으로 가고, 다음 스캔이 다시 씁니다.
심층 비교. 앨범에서 우클릭하거나, 설정에서 모든 앨범에 실행합니다. 기록이 있는 파일은 디스크만 읽어 검증하고 보관함은 건드리지 않습니다. 기록이 없는 파일(이 버전 이전부터 있던 백업)은 보관함이 내어주는 바이트와 대조하고 결과를 기록합니다 — 그래서 비싼 단계는 파일마다 평생 한 번입니다. 원본이 iCloud에만 있고 다운로드를 꺼 둔 사진은 정직하게 검증 불가로 표시합니다. 백업 도구가 당신 대신 낙관할 자격은 없습니다.
문제가 발견된 파일은 앨범 행에 그대로 표시되고, 옆에 버튼 하나: 손상된 파일 다시 백업. 나쁜 사본은 휴지통으로, 새 내보내기가 자리를 대신하고, 증거가 갱신됩니다. 원본이 불변이므로 복구는 언제나 무손실입니다.
내 백업을 망가뜨린 오후
기능이 완성되고, 실험을 했습니다.
먼저 Foldic이 테스트 드라이브에 331개 파일을 동기화하게 하고 첫 심층 비교를 실행했습니다. 이 331개는 "옛 백업" — 색인에 기록이 없는 — 이라 전부 보관함과 대조되었습니다: 331개 통과, 색인이 디스크에 기록되었습니다.
그다음 3.3 MB 사진 한 장을 골라 1,000,000번째 바이트를 뒤집었습니다. 파일 크기는 1바이트도 변하지 않았습니다. 날짜도 그대로. 이름도 그대로. 예전 Foldic의 눈에 이 사진은 영원히 "백업됨"입니다.
심층 비교를 다시 실행했습니다. 이번에는 색인에 331개의 기록이 있으니 전체 패스가 디스크만 읽고 6초에 끝났습니다 — 그리고 그 파일을 지목했습니다:
DAMAGED 15846A2E-…jpg: recorded 3315306B 06723ae2…, disk has 3315306B c87e7a45…
기록된 해시도, 디스크의 해시도, 제가 터미널에서 따로 계산한 값과 정확히 일치했습니다. 복구를 한 번 클릭하자 사진이 다시 내보내졌고, 세 번째 패스가 보고했습니다: 396개 파일, 전부 검증됨, 손상 0.
(이 실험은 도중에 제 버그도 하나 잡았습니다 — 색인이 샌드박스의 어떤 경로에서 저장에 실패하는 문제. 이런 테스트의 의미가 거기에 있습니다. 기능을 검증하고, 기능을 만든 사람도 검증합니다.)
백업의 가치는 복원하는 날 결정된다
"동기화 완료"는 약속입니다. 이 기능은 감사(監査)입니다 — 그리고 감사의 규칙은: 새로 쓴 것은 그 자리에서 검증하고, 오래된 것은 때맞춰 재검증하고, 검증할 수 없는 것은 검증할 수 없다고 소리 내어 말하는 것.
백업의 가치는 쓴 날에 결정된 적이 없습니다. 복원하는 날 결정됩니다. 그날 당신이 여는 모든 사진이 온전하기를 바랍니다.
부록: "원자적 이동"의 경계
정확히 말해 두자면. 로컬 파일 시스템(APFS, HFS+)의 rename은 원자적 시스템 콜이고, 이것이 "반쪽짜리 사진은 결코 남지 않는다"의 근거입니다. SMB도 프로토콜 층에서는 원자적 rename 의미론을 갖습니다 — 다만 그것은 서버 쪽 파일 시스템 이야기입니다. 실제 쓰기 경로에는 클라이언트와 서버 양쪽에 라이트백 캐시가 있습니다. 당신의 Mac이 "썼습니다"를 들었을 때, 데이터는 어느 쪽 메모리에든 남아 있을 수 있습니다. 그 틈에서 연결이 죽으면 디스크에 무엇이 남는지 프로토콜은 아무것도 약속하지 않습니다. 그래서 "다시 읽어 비교"는 누구의 응답도 믿지 않고 동기화 끝에 실행됩니다 — 다시 읽은 바이트만이 캐시의 보증도 프로토콜의 약속도 거치지 않은 유일한 증거이기 때문입니다.
(엄밀히는 다시 읽기조차 클라이언트 캐시에 맞을 수 있습니다. 그래서 심층 비교가 최후의 방어선입니다: 언제든 실행할 수 있습니다 — 재부팅 후에도, 다른 Mac에서도.)
언제부터 쓸 수 있나요? 쓰기 시 기록, 읽기 검증, 심층 비교는 0.8과 함께 출시되었습니다. 색인은 증거이지 권위가 아닙니다: 지워도 아무것도 망가지지 않습니다 — 다음 심층 비교가 보관함과 다시 대조할 뿐입니다.