cd /news/ai-agents/show-hn-apple-notes-photos-and-find-… · home › topics › ai-agents › article
[ARTICLE · art-145951] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Show HN: Apple Notes, Photos and Find My as Native Linux Apps (Omarchy/Arch)

Developer ferdousbhai released icloud-for-omarchy, a signed pacman repository that brings Apple Notes, iCloud Photos and Find My to Arch Linux and Omarchy as native apps sharing one Apple sign-in. The package set includes icloud-notes (Qt/QML with two-way Markdown sync via the Rust icloud-notes-sync engine), icloud-photos and icloud-findmy (both GTK4/libadwaita), and icloud-session, a D-Bus daemon and Rust client crate handling authentication. Every window action is also exposed as a terminal command with shared --json output, one JSON error shape and one exit-code table, with docs/AGENTS.md and a Claude Code skill provided so AI agents can drive the apps.

read8 min views1 publishedOct 6, 2026
Show HN: Apple Notes, Photos and Find My as Native Linux Apps (Omarchy/Arch)
Image: Michielbdejong (auto-discovered)

iCloud apps for Omarchy (and any Arch Linux), sharing one Apple sign-in, published as one signed pacman repository.

  • Notes : your Apple Notes as a folder of Markdown files, synced both ways. Edit, rename, move or delete them in the app, in Neovim or Obsidian, or withmv andrm , and iCloud follows.
  • Photos : browse, download, upload and delete your iCloud Photos.
  • Find My : your devices on a map, with play sound, Lost Mode and a location trail.
  • Every window action also works from the terminal, with --json output for scripts and AI agents.
curl -fsSL https://github.com/ferdousbhai/icloud-for-omarchy/releases/latest/download/install.sh | sudo bash

<sub>Screenshots use demo data.</sub>

Directory Package What it is
notes/ ,notes-sync/ icloud-notes Apple Notes as a Qt/QML app and the icloud-notes command, synced with iCloud by its engine, icloud-notes-sync (notes-sync/, in Rust, originally derived from icloud-md), which the package installs off PATH.
photos/ icloud-photos iCloud Photos in GTK4/libadwaita: browse, download, upload and delete.
findmy/ icloud-findmy Find My devices in GTK4/libadwaita: locate, play a sound, Lost Mode, history trail.
session/ ,sessiond/ icloud-session The shared sign-in: a D-Bus daemon, a sign-in window and a CLI (sessiond/), plus the Rust client crate every app links (session/).

The shared sign-in's design (the daemon, its D-Bus interface, the session files) is in session/README.md.

Everything the windows do can be done from a terminal, or by an AI agent: icloud-notes, icloud-photos and icloud-findmy take commands (icloud-notes list, icloud-photos download, icloud-findmy locate, ...) and run them without a window, and icloud-session owns the sign-in. They share --json output, one JSON error shape and one table of exit codes.

  • docs/AGENTS.md : the reference to give an agent (auth, commands with example JSON, safety rules, recipes).
  • docs/skills/icloud/SKILL.md : the same as a Claude Code skill; copydocs/skills/icloud into~/.claude/skills/ .
  • docs/CLI.md : every GUI feature mapped to its command, the exit and error codes, the JSON shapes.

Signing in is the one thing a person must do: Apple's page (password, 2FA) opens in a window from icloud-session sign-in.

  • You sign in on Apple's own page , password and two-factor code included, in a window opened byicloud-session sign-in . The apps never see your password.
  • What's kept is the session cookies, in~/.local/state/icloud-session/account.json , readable only by you.
  • Your password is stored only if you choose to , for Find My, which asks for it again from time to time:icloud-session set-password puts it in your system keyring (the Secret Service, e.g. GNOME Keyring), andicloud-session forget-password removes it.
  • The apps talk only to Apple , plus OpenStreetMap for Find My's map tiles.
  • Notes deletes are recoverable : a note you delete goes to Recently Deleted in iCloud (about 30 days).
  • It's all open source , and the packages are signed with a key whose fingerprint is pinned ininstall.sh .
curl -fsSL https://github.com/ferdousbhai/icloud-for-omarchy/releases/latest/download/install.sh | sudo bash

installs all three apps (icloud-notes, icloud-photos, icloud-findmy), which pull in icloud-session. To install only some, name them:

curl -fsSL .../install.sh | sudo bash -s -- icloud-photos icloud-findmy

The script (install.sh) trusts the package-signing key (after checking it against the fingerprint pinned in the script), adds the signed [icloud-for-omarchy] repository as /etc/pacman.d/icloud-for-omarchy.conf with an Include line in /etc/pacman.conf, installs an Omarchy pre-refresh-pacman hook that restores the repository after omarchy refresh pacman, and installs the packages in one pacman -Syu. Re-running it is safe. Updates then arrive with omarchy update.

Rather not pipe a script into sudo bash? These are the same steps, one at a time:

curl -fsSLO https://github.com/ferdousbhai/icloud-for-omarchy/releases/latest/download/icloud-for-omarchy-signing-key.asc
gpg --show-keys icloud-for-omarchy-signing-key.asc

sudo pacman-key --add icloud-for-omarchy-signing-key.asc
sudo pacman-key --lsign-key 35C47A06567940B6796B4D0F9B3C7BDF85268B31

printf '[icloud-for-omarchy]\nSigLevel = Required DatabaseRequired\nServer = https://github.com/ferdousbhai/icloud-for-omarchy/releases/latest/download\n' \
  | sudo tee /etc/pacman.d/icloud-for-omarchy.conf
echo 'Include = /etc/pacman.d/icloud-for-omarchy.conf' | sudo tee -a /etc/pacman.conf

sudo pacman -Syu icloud-notes icloud-photos icloud-findmy

On Omarchy, omarchy refresh pacman rewrites /etc/pacman.conf; the script installs a hook that adds the Include line back, so by hand you would re-add it after a refresh.

Machines set up from earlier Notes releases, which had a repository of their own ([icloud-notes]), are migrated: once [icloud-for-omarchy] is added, the script removes /etc/pacman.d/icloud-notes.conf, its Include line and its Omarchy hook. The icloud-notes-sync package of earlier releases needs nothing from the script: icloud-notes now carries the engine and replaces it, so the next omarchy update swaps it out.

Every release also carries one installer per app, install-notes.sh, install-photos.sh and install-findmy.sh: install.sh with its default set to that one app, generated by bin/make-installers. The website (not in this repository) redirects its one-liners to these assets: https://ferdousbhai.com/icloud/install.sh to install.sh, and https://ferdousbhai.com/icloud-<app>/install.sh to that app's installer, e.g. .../releases/latest/download/install-photos.sh, so curl ... | sudo bash keeps installing just that app. The apps' page is https://ferdousbhai.com/icloud.

To uninstall: omarchy pkg drop <packages>, then remove /etc/pacman.d/icloud-for-omarchy.conf, its Include line in /etc/pacman.conf, and ~/.config/omarchy/hooks/pre-refresh-pacman.d/icloud-for-omarchy.

Cargo.toml, Cargo.lock   one Cargo workspace: session, sessiond, notes-sync, photos, findmy
session/  sessiond/      icloud-session: client crate / daemon, sign-in window, CLI
notes/                   icloud-notes (qmake project, QML, tests, its own bin/build and bin/test)
notes-sync/              icloud-notes-sync, the sync engine the icloud-notes package ships
photos/  findmy/         icloud-photos, icloud-findmy
packaging/<package>/     one PKGBUILD per package
install.sh               the one installer (per-app copies are generated at release)
bin/                     build, test, release, verify-release, make-installers; dev-install/dev-uninstall for the daemon
tests/                   the add_signed_repo hash pin; install_test.sh, the installers against stubbed pacman
docs/                    the command-line reference (CLI.md, AGENTS.md, skills/)

Each directory kept its history: the five former repositories (ferdousbhai/icloud-session, icloud-notes-sync, icloud-photos, icloud-findmy and icloud-notes) were imported with git filter-repo into their subdirectories and merged, so git log --follow on a file reaches back past the move. icloud-notes' release tags v0.1.0... v0.3.8 are here as notes-v0.1.0... notes-v0.3.8.

Needs rust, sqlite, gtk4, libadwaita, libshumate (findmy) and webkitgtk-6.0 (the sign-in window) for the Rust crates, and qt6-base, qt6-declarative and make for Notes. The apps talk to the icloud-session daemon over D-Bus; for development without the package, bin/dev-install puts a release build of it in ~/.local/bin with a user D-Bus activation file (bin/dev-uninstall undoes it).

bin/build                       # every package; or name some: bin/build icloud-photos
bin/test                        # clippy, all Rust tests, notes/bin/test, the installer checks
tests/install_test.sh           # the installers alone: stubbed pacman, scratch /etc in a user namespace
cargo test -p icloud-findmy     # one crate
notes/bin/test                  # the Qt app's tests alone (they run on a private D-Bus)

Rust binaries land in target/release/, Notes in notes/build/. Each app can also run against a local fake of Apple's servers; its README says how.

notes-sync's recorded scenarios and golden corpora run with cargo test; ICLOUD_NOTES_SYNC_REGEN=1 cargo test -p icloud-notes-sync re-records them from the current code (see notes-sync/README.md).

Releases are cut from a checkout with the package-signing key in its keyring, no CI involved:

bin/release icloud-notes 0.4.1
bin/release icloud-session 0.3.0 icloud-notes 0.6.0   # several at once

Versions are per package and so are the tags: <name>-v<version>, where <name> is the package name without icloud- (session, photos, findmy, notes), e.g. notes-v0.4.1. Each PKGBUILD takes its pkgver from its own newest tag: at the tag it is the plain version, and a later commit builds <version>.r<count>.<sha> (0.0.0.r<count> for a package never tagged).

bin/release runs bin/test, sets the named packages' versions (PKGBUILD, and Cargo.toml for the Rust ones), commits and tags them, and builds only those packages with makepkg from packaging/, each from the committed HEAD via git archive. Every other package is downloaded from the latest release, its signature checked against the pinned key, and carried forward unchanged, so the new repository database, icloud-for-omarchy.db, always lists all four. It signs the database with the key whose fingerprint install.sh pins and publishes it, the packages, the public key, install.sh and the per-app installers as one GitHub release on the first tag named; releases/latest/download resolves to it.

The Notes sync engine (notes-sync/) is not released on its own: it ships inside icloud-notes, built from the same commit, so releasing icloud-notes releases it, and its Cargo.toml version only names the engine (icloud-notes-sync --version). The notes-sync-v* tags are historical, from when it was the separate icloud-notes-sync package (last notes-sync-v0.2.0); the first icloud-notes that carries it must be released before any other package, and bin/release refuses to carry forward an icloud-notes that still depends on the old package.

A release counts as shipped only once bin/verify-release has installed each named package in a clean Arch container, the apps through their per-app installers and the shared packages through install.sh, and found that version installed; otherwise bin/release deletes the release and the tags. With PUBLISH_CRATE=1, releasing icloud-session also publishes its client crate to crates.io after the release is verified, unless crates.io already has that version; by default it does not.

The add_signed_repo function in install.sh is shared verbatim with the Ghost installer (ferdousbhai/ghost), and both repositories pin its hash in their tests (tests/add_signed_repo.sha256 here): change it in both places, and both hashes, together.

One key signs these packages and Ghost's; its fingerprint is pinned in both installers and it lives only in the releasing machine's keyring, protected by a passphrase. Losing it would break the trust chain on every machine that installed from these repositories, so keep an encrypted backup somewhere off this machine:

gpg --armor --export-secret-keys 35C47A06567940B6796B4D0F9B3C7BDF85268B31 \
  | gpg --symmetric --armor --output package-signing-key.backup.asc

Restoring is gpg --decrypt package-signing-key.backup.asc | gpg --import.

To rotate the key: generate the new one, publish one release from each project signed with the old key that also ships the new public key as <repository>-signing-key.asc, update the pinned fingerprint in both installers and the tests, then sign the next releases with the new key. Machines that installed earlier pick up the new key by re-running the one-liner, which is idempotent.

MIT, see LICENSE. Third-party credits (icloud-md, node-diff3, the mdast/micromark utilities, yaml) are in NOTICE.

── more in #ai-agents 4 stories · sorted by recency
── more on @ferdousbhai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-apple-notes-…] indexed:0 read:8min 2026-10-06 · —