iphonephotobackup.com - GET /how.html - 200 OK

How it works

Back to the backup

How it works

A short tour for people who like cables, packets, and not sending their camera roll to a cloud.

Nothing leaves this computer

The page is a static bundle: HTML, JavaScript, and a Zig program compiled to WebAssembly. After it loads, it does not need the internet. USB talks to the phone. The File System Access API writes into the folder you picked. Pairing keys stay in this browser's IndexedDB. There is no account, no server copy, no analytics.

To verify: open DevTools, Network, start a backup, and watch that the only traffic (if any) is this page. Or toggle airplane mode after load.

The path from the cable to a folder

  1. WebUSB claims Apple's USB mux interface (class 255 / 254 / 2) on the iPhone. That is the same pipe iTunes and Finder use. We never reset the USB bus, because that can revoke "Trust this computer."
  2. USB mux is Apple's fake-TCP over bulk endpoints (the usbmuxd on-wire protocol, not the usbmuxd daemon). We speak it in Zig: version handshake, then connections to lockdown (port 62078) and later AFC.
  3. lockdownd is the phone's gatekeeper. First we read public fields: the name you gave the phone, iOS version, model, UDID. Then we pair. That is the Trust dialog. Pairing uses a host-side RSA-2048 identity. The TLS session for later RPCs uses the root cert from that pair record.
  4. StartService asks lockdownd for com.apple.afc. AFC is Apple File Conduit: a simple RPC to list directories and read files, jailed to the media tree (/DCIM, /PhotoData).
  5. Copy streams file bytes over USB into the folder you chose, as full originals (HEIC, JPEG, MOV, AAE, the lot). Files already present are skipped. A side queue builds small JPEG previews for the "just saved" strip. That queue never substitutes for the real file.

Why it's Zig in a Web Worker

The mux/lockdown/AFC state machine is synchronous: it blocks on USB reads. Browsers refuse to block the UI thread, so the Zig WASM runs in a worker and waits with Atomics.wait on a SharedArrayBuffer. The page thread owns WebUSB and the folder handle, and replies to the worker. That is also why this page is served with COOP/COEP (cross-origin isolation). SharedArrayBuffer requires it.

Chrome and Edge on localhost or HTTPS. Firefox's WebUSB support is not enough for this.

Trust, pairing, and what is stored

The first time, iOS shows Trust This Computer. We generate a pairing record (certificates + host id) and save it only in IndexedDB under this origin. Next visit, the same browser can skip the dialog unless you wiped site data. That record never goes to a server. Clearing this site's data in the browser forgets the pairing.

Speed and ETA

USB 2.0 High Speed tops out around 35 to 40 MiB/s of useful payload in practice. The speed you see is an exponential moving average of bytes actually written, so a single huge video does not make the number jump around. Time remaining is remaining bytes divided by that smoothed rate, then smoothed again so "38 minutes" does not flicker to 12 and back.

What we deliberately don't do

  • No usbmuxd, libimobiledevice, or FUSE on the host.
  • No USB bus reset (that drops Trust).
  • No cloud, no telemetry, no phone-home.
  • Albums and people in the UI come from folders and Photos.sqlite in your destination directory after a copy, not from a remote API.

Back to the backup

Static files. No server copy of your photos. GPL-3.0