npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@evantahler/icloud-backup

v0.10.0

Published

Append-only backup of iCloud Photos, Drive, Notes, and Contacts to any local directory.

Readme

icloud-backup

Append-only backup of iCloud Photos, Drive (Desktop & Documents), Notes, and Contacts to any locally-mounted directory. macOS-only. No network calls to Apple — reads local SQLite databases directly via macos-ts.

Four-lane backup in progress

icloud-backup all /Volumes/icloud-backup-evan

Install

# Standalone binary (darwin-arm64 or darwin-x64)
curl -fsSL https://raw.githubusercontent.com/evantahler/icloud-backup/main/install.sh | bash

Prerequisites (one-time)

  1. Photos.app → Settings → iCloud → "Download Originals to this Mac"
  2. System Settings → Apple ID → iCloud → iCloud Drive → "Desktop & Documents Folders" = on
  3. Full Disk Access for your terminal app (System Settings → Privacy & Security → Full Disk Access)
  4. Mount your destination(s) at stable paths

Run icloud-backup doctor to verify all of the above before your first backup.

icloud-backup doctor

Usage

icloud-backup <command> [options]

Per-service backup commands (each takes a destination directory):
  photos    <dest>     back up Photos library originals → <dest>/photos/
  drive     <dest>     back up iCloud Drive Desktop & Documents → <dest>/drive/
  notes     <dest>     back up Apple Notes as markdown → <dest>/notes/
  contacts  <dest>     back up Apple Contacts as vCard or JSON → <dest>/contacts/
  all       <dest>     run all four → <dest>/{photos,drive,notes,contacts}/

Other:
  doctor [dest]        run preflight checks and exit
  rebuild <dest>       walk destinations and rebuild manifests
  check-update         force a fresh npm-registry check, print result, exit
  upgrade              upgrade to the latest published version (in-place)
  --help, -h
  --version, -v

Environment:
  ICLOUD_BACKUP_NO_UPDATE_CHECK=1   suppress the background "update available" notice

Run an individual service to a different destination than all:

icloud-backup all /Volumes/main
icloud-backup photos /Volumes/photo-archive

How it works

  • Photos: iterates the Photos SQLite library, copies originals + a JSON metadata sidecar, copies the .mov companion for Live Photos.
  • Drive: brctl download materializes Desktop & Documents, then walks them with Bun.Glob and copies changed files.
  • Notes: iterates Notes, writes each as a markdown file with attachments in a sibling .attachments/ directory.
  • Contacts: iterates Contacts, writes one JSON file per contact, sha256 of contents is the change key.

State (manifests, lock, update cache) lives at ~/.icloud-backup/ regardless of where backups land — keeps SQLite local-fast and survives unmounted destinations.

When a source changes, the existing destination file is moved to <dest>/_overwritten/<date>/v<n>/ before the new version is written. Append-only.

Resume & rebuild

Crash-safe: writes are atomic (write-to-tmp, fsync, rename); the manifest upsert is the last step per file. If the manifest is lost, run icloud-backup rebuild <path> to walk destinations and reconstruct it.

Output layout

/Volumes/icloud-backup-evan/
├── photos/2024/01/IMG_0001.HEIC + IMG_0001.HEIC.json
├── drive/{Desktop,Documents}/...
├── notes/<folder>/<title>-<id>.md (+ .attachments/ sibling)
├── contacts/<displayName>-<id>.json
└── _overwritten/<date>/v<n>/...

Destination compatibility

The destination can be any locally-mounted directory: APFS-formatted external SSD, exFAT USB stick, or an SMB share to a NAS. Filenames coming out of iCloud sometimes contain characters or lengths that work on macOS-local filesystems but get rejected on shares — long DALL·E prompt-named PNGs, decomposed-Unicode names, trailing dots, etc.

Rather than supporting each filesystem's quirks separately, all destination paths are sanitized to fit the strictest commonly-deployed SMB share (the lowest common denominator). A backup that works to a local disk will also work when the same destination is later moved to a NAS.

| Constraint | SMB (worst-case observed) | APFS / HFS+ | |-------------------------------------|----------------------------------|-------------| | Filename byte length per component | 143 bytes (HVTVault probe)* | 255 bytes | | Total path length | 1024 bytes (PATH_MAX) | 1024 bytes | | Trailing dot or space in name | rejected | allowed | | Reserved chars (\ : * ? " < > \|) | rejected | allowed | | Filename encoding | NFC (UTF-16 on the wire) | NFD | | Leading dot | allowed but hides on Unix | allowed |

* The 143-byte ceiling is server-specific — some Samba builds cap at 255 UTF-16 chars, others lower. The byte cap is therefore probed at the start of each lane by binary-searching test writes against the destination root. The discovered cap is logged at run start (e.g. destination NAME_MAX=143, sanitizing filenames to 126 bytes).

Truncated filenames keep their extension when possible and never split a multi-byte UTF-8 codepoint. Decomposed names are NFC-normalized so DALL·É round-trips identically on both ends.

Contributing

Screenshots and GIFs in this README are regenerated by bun run capture — see docs/captures.md for the VHS + fake-source pipeline.

Development

If you have Bun installed and prefer to run from the npm registry instead of the standalone binary:

bun install -g @evantahler/icloud-backup

Or run the latest version without installing:

bunx @evantahler/icloud-backup all /Volumes/icloud-backup-evan

License

MIT © Evan Tahler