▶ Website · Launch video · Explainer deck · CLI preview
Rich enough for the Mac, not rich enough for the 2 TB upgrade? Same. So I put my
Documents,Downloads,Desktopand projects on a cheap external SSD — and got tired of everything breaking every time the cable slipped. limpet is the fix.
A limpet is the little mollusk that clamps tight to its rock, yet detaches and re-attaches cleanly with the tide. That's the idea: your folders cling to the external drive, but they survive every unplug and re-attach themselves when the drive comes back.
~/Documents ─▶ /Volumes/X9/Documents drive connected → you work straight off the SSD
~/Documents ─▶ (real local folder) drive unplugged → saving still works, no errors
~/Documents ─▶ /Volumes/X9/Documents drive back → offline work merged in, never overwritten
Single file. Pure bash. No kernel extensions, no daemons, no network. ~690 lines, still one file, still zero dependencies — and now with a live progress UI (see the CLI preview).
Macs ship with small, expensive internal storage. So a lot of us put Documents, Downloads, Desktop, and project folders on a cheap external SSD and symlink to them. It works great — until the drive is unplugged.
The moment that happens, a plain symlink points to nothing:
- saves fail and apps throw errors,
- your Desktop and Documents look empty,
- and a reboot can silently recreate empty folders, splitting your files across two places.
macOS has never had a "work offline, sync when it's back" mechanism for external volumes (the way Windows has Offline Files). So the usual options are: babysit the cable, or give up and cram everything back onto the internal disk.
limpet makes the external drive your primary storage and adds an automatic safety net:
- Connected → your folders are symlinks straight to the drive. Zero overhead, full speed.
- Unplugged → within a second, limpet turns each folder back into a real local folder so every save keeps working.
- Reconnected → limpet moves whatever you created while offline back onto the drive, keeping both copies on any name clash — it never overwrites.
It's offline mode for your external drive.
# 1. install
curl -fsSL https://raw.githubusercontent.com/notpritam/limpet/main/install.sh | bash
# 2. set it up (interactive: pick a drive, pick folders)
limpet setup
# 3. check anytime
limpet statusThat's it. limpet setup moves the folders you choose onto the drive (verified copy first — see Safety), replaces them with symlinks, and installs the background guard plus a terminal hook. Open a new terminal afterward to activate the hook.
Prefer not to pipe to bash? Clone the repo and run
./install.sh, or just copy the singlelimpetfile anywhere on yourPATH.
Two small, cooperating pieces — that's the whole tool.
A launchd agent with WatchPaths /Volumes. macOS fires it the instant any drive mounts or unmounts, so limpet reacts to a plug/unplug within a second:
- drive gone → symlinked folder becomes a real local folder (saves keep working),
- drive back → folder becomes a symlink again, after merging anything you saved offline.
It only stats the mountpoint and does local symlink swaps, so it needs no Full Disk Access.
macOS security (TCC) blocks background agents from writing to external volumes. So the part that copies your offline work back onto the drive runs from your terminal instead — which is allowed — via a tiny throttled hook in your shell rc. It also refreshes an offline-readable mirror of any critical paths you nominate, and does the daily update check.
plug/unplug event
│
┌────────▼─────────┐ ┌───────────────────────┐
│ guard (launchd) │ ◀──── │ terminal hook (rc) │
│ instant failover │ │ merge-back + mirror │
└───────────────────┘ │ + daily update check │
└───────────────────────┘
A mkdir-based lock means the two never collide.
| Command | What it does |
|---|---|
limpet setup |
Interactive wizard: pick a drive + folders, move them over, install the guard. |
limpet status |
Drive + folder state. --json for machine-readable output. |
limpet watch |
Live view of drive + folder state; redraws on plug/unplug (Ctrl-C to quit). |
limpet list |
Managed folders + any ad-hoc links you've made into the drive. |
limpet link <path> |
Move any extra file/folder onto the drive and symlink it back. |
limpet unlink <path> |
Bring a symlinked path back onto the local disk. |
limpet doctor |
Health-check and offer fixes (agent loaded? drive renamed?). |
limpet sync |
Run the failover/failback + mirror right now. |
limpet update |
Update limpet to the latest release (--check only checks). |
limpet uninstall |
Remove the guard + hook (optionally restore folders to local). |
Global flags: --yes (never prompt), --dry-run (print actions, change nothing).
limpet is built so a person can drive it interactively and a script or AI agent can drive it non-interactively — same tool, no GUI required.
Interactive (you):
limpet setup # answers a couple of promptsNon-interactive (scripts / agents):
limpet setup --drive /Volumes/X9 --folders Documents,Downloads,Desktop --yes
limpet link /Volumes/X9/Projects/my-app --yes
limpet status --jsonYou're not limited to Documents/Downloads/Desktop. Point anything at the drive:
limpet link ~/Dev # move ~/Dev onto the drive, leave a symlink
limpet link ~/Movies/4k.mov # single files work too
limpet list # see everything that's linkedAlready symlinked things by hand? limpet setup and limpet link detect existing links and adopt them instead of re-copying.
limpet update # check GitHub Releases and install the latest
limpet update --check # just tell me if there's a newer versionTurn on automatic checks during limpet setup (or set AUTO_UPDATE=1 in the config). With it on, limpet checks once a day from your terminal and either applies the update silently or shows a one-line notice — your choice. Every downloaded version is syntax-checked before it replaces the running binary.
- Verified copy before delete. When moving a folder onto the drive, limpet does a
dittocopy, then byte-verifies a manifest of every file before removing the original. If verification fails, the original is left untouched. - Never overwrites. On reconnect, a file that exists in both places is kept as
name.local-<timestamp>— you never lose either version. - No elevation, no extensions. No kernel extensions, no background servers, no network calls (except the optional update check). It's ~690 lines of bash you can read in one sitting.
- Not a backup. limpet keeps your data available and resilient to unplugs — it is not a substitute for Time Machine or an offsite backup. Keep one.
Wait — is this safe? Will I lose files? No. limpet byte-verifies every copy before it deletes the original, and if two files ever clash it keeps both, timestamped. It's not a backup, though — keep your Time Machine.
Who is this actually for? Honestly? Me. It runs on my machine every day. I made it public because you're probably in the same boat — small internal disk, big external SSD. If it helps, ⭐ the repo or say hi.
Does it work with any drive and any folders?
Any external volume, and whatever home folders you pick. limpet link <path> moves anything else onto the drive too.
Do I have to babysit it? No. The guard + terminal hook handle everything. Unplug, replug, reboot with the drive gone — it sorts itself out.
- Eject cleanly. If a drive is yanked without ejecting, macOS may remount it as
"X9 1". limpet keys off the exact mount path, solimpet doctorwill flag this — eject and replug to restore the name. - The drive still holds your data. Failover keeps you working; it doesn't conjure the files that live on an absent drive. Nominate critical paths for the offline mirror if you need them readable while unplugged.
- macOS only. It relies on
launchd,ditto, and APFS behavior.
limpet uninstall # removes the guard + terminal hook
# offers to turn folders back into normal local foldersYour config stays at ~/.config/limpet/ until you delete it.
limpet— the entire CLI, one pure-bash file.install.sh— the curl-able installer.test/test-guard.sh— real-filesystem end-to-end test (no mocks): symlink → unplug → offline edit → replug → merge-back with keep-both. Run it withbash test/test-guard.sh.share/com.limpet.guard.plist.tmpl— the LaunchAgent, shown for transparency.AGENTS.md— architecture + invariants for humans and AI agents working on the code.
Contributions welcome — see CONTRIBUTING.md.
I bought a Mac, looked at Apple's storage-upgrade prices, quietly closed the tab, and bought an external SSD instead. Then it kept breaking every time the cable moved. This is the tool I wish had existed that afternoon. It's a personal project — I run it on my own machine — but I made it public so anyone in the same spot can use it. Built by Pritam · blog.
MIT © Pritam Sharma