Jamal27 downloadsDetect your device's ecosystem, check the vault sits in a folder iCloud or Google Drive can sync, compare vaults across devices, and resolve sync conflicts.
Check that your Obsidian vault is set up to sync across your devices, confirm the sync actually worked, and clean up the duplicate files a sync engine leaves behind.
jemzsync works out which ecosystem it is running in and checks the vault against the right cloud: iCloud Drive on Apple devices, Google Drive on Windows and Android. While Obsidian is open it watches the vault and re-checks within seconds of anything changing — including files arriving from the other device.
Works on macOS, iPadOS, iOS, Windows, Android and Linux.
iCloud Drive moves your files. jemzsync tells you whether it is working.
An Obsidian plugin cannot sign in to an Apple Account, and jemzsync does not try. Reading or writing iCloud on your behalf requires a signed native app holding Apple's iCloud entitlement, granted only to apps built with a paid Apple Developer account. Plugins are JavaScript running inside Obsidian's sandbox: no entitlement, no Apple Account access, and on iPhone and iPad no access to any file outside the vault.
So jemzsync never asks for your Apple Account and never sees your password.
It does not contact iCloud or Google Drive either — it cannot. The one network destination it ever reaches is api.github.com, and only if you choose GitHub storage and connect an account yourself. Leave that switched off, as it is by default, and the plugin makes no network requests at all.
What it does instead is fix the reason your notes are not showing up. Apple's sync already works — the usual problem is that the vault is in a folder your iPhone cannot see. jemzsync finds that, tells you exactly how to fix it, and then proves the fix worked.
| Handled by | |
|---|---|
| Moving file contents between devices | iCloud Drive / Google Drive |
| Saving what you type into the vault | Obsidian, as you type |
| Signing in to your Apple or Google Account | The Settings app, once per device |
| Creating or switching vaults | You, in Obsidian's vault picker |
| Working out which cloud this device should use | jemzsync |
| Checking the vault is in a folder that cloud can reach | jemzsync |
| Warning you up front when it is not | jemzsync |
| Watching the vault and re-checking as things change | jemzsync |
| Proving both devices hold the same notes | jemzsync |
| Finding files the cloud has not downloaded yet | jemzsync |
| Resolving duplicate copies after an offline edit | jemzsync |
These are worth stating plainly, because they sound reasonable and are not possible from inside Obsidian:
Almost always one of these three.
1. The vault is in the wrong iCloud folder. This is the big one. Obsidian on iPhone and iPad only lists vaults from its own private iCloud container:
iCloud Drive/Obsidian/YourVault
A vault in iCloud Drive/Documents/YourVault, or any other iCloud folder, syncs perfectly well — you will see the files in the Files app — but mobile Obsidian will never list it. The folder must be the one carrying the Obsidian icon. A folder you create by hand and name "Obsidian" will not work; only the app can create the real one.
2. The vault is stored locally. A vault under ~/Documents, ~/Desktop or anywhere else on the Mac has nothing replicating it. Turning on "Desktop & Documents Folders" in iCloud settings does not help — mobile Obsidian still only reads its own container.
3. iCloud offloaded the files. To save disk space, iCloud replaces file contents with small placeholder stubs named like .YourNote.md.icloud. Obsidian cannot read a placeholder. The note appears missing or empty even though sync is working.
jemzsync detects all three.
There is no build step. main.js is plain JavaScript that Obsidian loads directly, which is also why the same files work on iPhone and iPad where you cannot run a compiler.
main.js, manifest.json and styles.css from the latest release.YourVault/.obsidian/plugins/jemzsync/.
The folder name must be exactly jemzsync.If your vault is already syncing through iCloud, the plugin arrives on its own — it lives inside the vault folder. Just enable it in Settings → Community plugins.
If it is not syncing yet, fix that first using the steps below, then enable the plugin.
If you have the BRAT plugin, add jamalbalya/jemzsync and it will install and auto-update for you.
Do this once. Back up your vault before you start — copy the whole folder somewhere outside iCloud.
Step 1 — Same Apple Account everywhere. On each device, open Settings and confirm you are signed in to the same account with iCloud Drive turned on.
Step 2 — Create the container from your iPhone. This step is the one people skip, and skipping it is why the rest fails.
This is what creates the real Obsidian folder in iCloud Drive. Only the app can create it correctly.
Step 3 — Move your Mac vault into that folder. Open jemzsync on the Mac and press Scan now. If the vault is in the wrong place, the panel gives you the exact Terminal commands, ready to copy. They back up first and never delete your original.
Or do it in Finder: copy the vault folder into iCloud Drive → Obsidian.
Step 4 — Open the copy on the Mac. In Obsidian choose Open folder as vault, then pick iCloud Drive → Obsidian → YourVault. Wait for the first sync to finish before editing anything on a second device.
Step 5 — Keep it downloaded. In Finder, right-click the Obsidian folder in iCloud Drive and choose Keep Downloaded. This stops iCloud from offloading your notes into placeholders.
Step 6 — Open the same vault on the iPhone. Obsidian will now list it. Open it and let it finish downloading.
Step 7 — Confirm it worked. See below.
Once jemzsync is enabled in the same vault on two devices, they find each other on their own — no server, no account, no setup.
Each device writes one small file into a hidden .jemzsync folder inside the vault, announcing its name and a fingerprint of its files. iCloud carries that file across with your notes. The panel's Devices card then shows every device in this vault, when it last checked in, and whether its files match this one — line by line:
Beacons are excluded from the fingerprint itself, so writing one never triggers a false mismatch, and are refreshed at most every 5 minutes so iCloud isn't churned. Turn announcing off in settings if you prefer.
The comparison fields fill themselves in. Once another device has announced itself, its name and fingerprint appear in Other device fingerprint and Other device name in settings on their own — there is nothing to copy across.
Both fields stay editable. Type into one and it is yours: nothing will overwrite it, on any later scan. Clear it and jemzsync starts filling it in again. There is a Detect again button beside each if you want to hand both back at once.
You can still do it by hand if you prefer: Copy vault fingerprint on one device, paste into Other device fingerprint on the other. Matching codes mean identical notes.
If they do not match, jemzsync tells you which device has more files. Wait a few minutes for iCloud and scan again.
The fingerprint deliberately ignores things that differ between devices for harmless reasons: modification times (iCloud restamps files), .obsidian/workspace* (your pane layout is meant to be per-device), .DS_Store, and the .jemzsync folder itself. Without those exclusions two perfectly synced devices would report a mismatch.
When two devices edit the same note before seeing each other, iCloud keeps both — as Note 2.md, or a name containing "conflicted copy". Nothing is lost, but you end up with duplicates.
jemzsync lists each one and gives you two choices:
Both send removed files to the trash rather than deleting them, so a wrong choice is recoverable.
Chapter 2.md is only ever flagged when Chapter.md exists in the same folder. Numbered notes on their own are left alone.
Open the command palette and search for jemzsync.
| Command | What it does |
|---|---|
| Open panel | Opens the sidebar |
| Check iCloud setup | Scans and reports whether the vault is in the right place |
| Scan for sync conflicts | Looks for duplicate copies |
| Copy vault fingerprint | Copies the fingerprint for comparison |
There is also a cloud icon in the left ribbon and a status bar summary.
| Setting | Default | What it does |
|---|---|---|
| This device's name | Detected (Mac, iPhone, Windows PC…) | How the device introduces itself to the others. A second device with the same name becomes "Mac 2" on its own. Stored per device, never synced |
| Announce this device | On | Writes the small beacon file other devices read |
| Watch the vault while Obsidian runs | On | Rescans a few seconds after anything changes, including files arriving from the cloud. Needs a restart |
| Warn when the vault cannot sync | On | Popup on launch if the vault is somewhere the cloud cannot reach |
| Scan when Obsidian starts | On | Checks the vault on launch |
| Scan every | 15 minutes | Background scans. 0 means on demand only |
| Notify about conflicts | On | Shows a notice when duplicates appear |
| Show status bar item | On | One-line summary. Needs a restart |
| Other device fingerprint | Detected | Filled in from the other device's announcement. Editable; clear it to re-enable detection |
| Other device name | Detected | Filled in from the other device's announcement. Editable |
Settings → jemzsync → Where this vault is stored. Three choices:
| Choice | What carries your notes |
|---|---|
| This device's cloud only (default) | iCloud Drive on Apple, Google Drive elsewhere. GitHub is never contacted. |
| Cloud and GitHub | The cloud moves your notes between devices; GitHub additionally keeps a full history you can go back through. |
| GitHub only | GitHub moves your notes. The vault can sit anywhere on disk. |
The choice is per device. Each device decides how it stores this vault, because in GitHub mode there is no shared settings file to read the answer from until after the first sync. Setting one device to "cloud only" does not stop another from syncing to GitHub — change it on every device you want changed.
GitHub only is the answer to a vault spanning ecosystems. A Mac and a Windows PC have no good shared consumer cloud — iCloud on Windows can duplicate files, and Google Drive on Android needs a second app. A Git repository works identically on all of them.
With GitHub storage on, jemzsync sends your changes about ten seconds after you stop typing, and checks for other devices' work every few minutes. Both are adjustable, and both can be switched off in favour of a Sync now button.
Receiving is a poll, not a push, and it is worth being clear why: GitHub has no way to notify a plugin that something changed. Sending is immediate because that is the half that can lose work if the device goes away; receiving can wait a few minutes.
Nothing is thrown away. The version from the other device is written alongside yours as Note (github conflicted copy 2026-08-02).md, and the conflicts card resolves it with the same Keep newest / Merge both buttons it uses for iCloud duplicates.
Several rules worth stating plainly, because each one exists because the alternative lost a file during testing:
.icloud stub whenever it wants disk space back; those are treated as present-but-unreadable, never as gone..trash folder. Nothing is ever removed outright.Your notes and attachments, and by default the rest of .obsidian/ — themes, snippets and other plugins — so a new device comes up already configured. Switch on Notes only to send just the markdown.
Four things are never sent, whatever the setting: any plugin's data.json (they hold API keys), .obsidian/workspace* (per-device layout), the .jemzsync announcements, and jemzsync's own files. That last one is deliberate: Obsidian's developer policy forbids a plugin distributing or updating itself, so a device in GitHub-only mode needs jemzsync installed by hand.
jemzsync talks to GitHub's REST API, which authenticates with a fine-grained personal access token in an HTTP header. SSH keys are for the git command line, which speaks a different protocol over a raw TCP socket — something an Obsidian plugin has no access to on any platform, and could never have on iOS or Android.
Create a token with Contents: read and write on the one repository you want to use, and paste it in. The field is masked, with an eye to reveal it.
The token is stored through app.saveLocalStorage — on that device, never in the vault. This matters more than it sounds: saveData writes into the vault, and the vault is both replicated by iCloud and committed to the repository. A token kept there would be copied to every device and then published into the repo it unlocks.
jemzsync reads Obsidian's platform flags on load and picks the cloud that ecosystem can actually use. You do not configure this.
| Device | Cloud checked for | Vault belongs in |
|---|---|---|
| Mac, iPhone, iPad | iCloud Drive | iCloud Drive/Obsidian/YourVault |
| Windows | Google Drive | My Drive\YourVault (any drive letter) |
| Android | Google Drive | see the caveat below |
| Linux | Google Drive | whatever folder your client watches |
OneDrive and Dropbox are recognised too. If your vault is already inside one of those, jemzsync reports it as fine rather than nagging you to move it — they replicate a folder just as well as Google Drive does.
iCloud Drive on Windows is a special case. iCloud for Windows exists, and it does replicate the folder, so your notes genuinely travel between an iPad and a PC. But Obsidian's own documentation warns that iCloud Drive on Windows can duplicate or corrupt files, and no plugin can prevent that. jemzsync therefore reports it as syncing but risky rather than pretending it is either fine or broken — and the conflicts card is where the damage shows up when it happens. If you need a PC in the mix, Google Drive or Obsidian Sync is the safer foundation.
The Android caveat. Google Drive is not the equivalent of iCloud here. The Drive app does not expose a folder that Obsidian can read and write continuously, so putting the vault "in Google Drive" on Android does not work the way it does on Windows. The workable options are a folder-sync app (FolderSync, Autosync for Google Drive) pointed at the Drive folder, or Obsidian Sync. jemzsync says so directly rather than sending you in a circle.
With Watch the vault on, jemzsync subscribes to Obsidian's vault events and rescans about eight seconds after the last change. That covers files you edit and files that arrive from your other device, so the panel and the fingerprint stay current without waiting for the 15-minute poll.
The eight-second delay is deliberate: it collapses a burst of typing, or a batch of files landing from the cloud, into a single scan.
One thing this must never do is react to its own beacon. Writing a beacon is a vault change, so an unfiltered watcher would scan, write a beacon, notice the write, and scan again — forever, on every device at once. Beacons are therefore excluded twice over, by path prefix and by an explicit beacon check, and both halves are asserted by tests and by a mutation that removes the guard.
The vault does not appear on my iPhone. It is in the wrong folder. It must be inside iCloud Drive/Obsidian/ — the folder with the Obsidian icon. Follow Step 2 above to create it properly.
Notes appear but are empty. iCloud offloaded them. jemzsync reports these as "not downloaded". On the Mac, right-click the Obsidian folder and choose "Keep Downloaded". On the iPhone, open the vault in Files and pull down.
Changes take a long time to appear. iCloud syncs on its own schedule and slows down on battery or a poor connection. Opening the Files app on the iPhone often prompts it along. If a device is stuck for hours, toggle iCloud Drive off and on in Settings.
I keep getting conflicts. Let one device finish syncing before editing on another. Fully closing Obsidian on the device you are leaving helps it flush changes.
The plugin does not appear after installing. Check the folder is named exactly jemzsync and contains all three files, then use Reload plugins.
Fingerprints will not match. Confirm both devices really opened the same vault in the iCloud folder, not two separate vaults with the same name. Also check for files still downloading.
api.github.com, and nothing else. Only when you turn on GitHub storage and connect an account. No analytics, no telemetry, no other host, ever. With GitHub off, the plugin makes no requests.app.saveLocalStorage, never through saveData — because saveData writes into the vault, and the vault is replicated by iCloud and committed to the repository. A token kept there would be copied to every device and then published into the repo it unlocks. It is shown masked and never logged..obsidian/plugins/*/data.json — other plugins keep API keys there, and a commit cannot be taken back — plus .obsidian/workspace*, .jemzsync/, .trash/, .git/ and .DS_Store. The list is asserted rule by rule in the tests, so dropping one is caught..jemzsync/ containing a device name you chose, a random ID, and file counts — never note contents, never an email or account.Obsidian's automated review flags both of these. Neither is accidental.
Clipboard. The plugin writes to the clipboard, never reads it. It happens only when you press a button: Copy on the fingerprint, Copy commands on the migration steps, or the Copy vault fingerprint command. What lands there is a sixteen-character digest or a block of shell commands. Nothing is read back, and nothing is copied without a click.
Per-device state kept out of saveData. Obsidian's saveData writes into the vault — and this vault is being replicated by iCloud or Google Drive, which is the entire point of the plugin. Two pieces of state must not travel:
saveData, the first device's ID would sync to the second, both would claim the same identity, and the Devices panel could never tell them apart.Both go through app.saveLocalStorage / app.loadLocalStorage, Obsidian's own API for exactly this: stored on the device, never synced, and scoped per vault — so two vaults open on the same Mac get separate identities rather than quietly sharing one. Everything that should be shared — scan preferences, exclusions — goes through saveData as normal.
saveData in 1.4.0, when they started filling themselves in. "The other device" is a different device depending on which one you are standing at, so a single shared value cannot be right for both. Worse, it could never settle: the Mac would write the iPhone's digest into the shared file, iCloud would carry it to the iPhone, which would overwrite it with the Mac's — and because that file lives in the vault, every one of those writes changed the fingerprint the two devices were trying to match on.Nine tests drive this against a fake app, including that two vaults never collide, that a dismissal in one does not silence another, and that a build without the storage API degrades to an ephemeral ID instead of throwing.
git clone https://github.com/jamalbalya/jemzsync.git
cd jemzsync
npm test
327 tests, no dependencies, no build step. The suite covers vault-location detection, the migration plan, conflict grouping and resolution, fingerprinting, device beacons, pairing auto-fill, device naming, ecosystem-neutral wording, and the scanner driven by a fake adapter — including end-to-end simulations of a Mac beacon being read on an iPhone for both the matching and the missing-note case.
The suite is itself verified by mutation testing (npm run test:mutation): 77 deliberate regressions are injected into a temporary copy of the source and all 77 must be caught — including an infinite-loop hang, an auto-filled field overwriting something you typed, Apple wording creeping back into a screen every platform sees, a force-push that would erase another device, an offloaded file being mistaken for a deleted one, and a delete falling back to a permanent removal instead of the trash.
Layout:
main.js plugin + core logic (no build step)
manifest.json plugin metadata
styles.css panel styling via Obsidian theme variables
test/test-core.js test suite
test/mutation.js mutation testing of the suite itself
.github/workflows/ tag-triggered release pipeline (tests gate the release)
main.js splits into a pure core (no I/O, fully tested), a scanner using Obsidian's adapter, and the Obsidian integration. module.exports.__core exposes the core for testing.
Uses no Node.js or Electron APIs, so isDesktopOnly is false and it runs on iOS and iPadOS.
jemzsync makes iCloud reliable, but iCloud has real limits: Obsidian does not control it, and heavy editing on several devices at once causes conflicts. Alternatives:
MIT