Marcos12 downloadsEnd-to-end encrypted bidirectional sync of your vault with Proton Drive, with three-way conflict remediation for offline edits.
Bidirectional sync between an Obsidian vault and a folder in Proton Drive, built on Proton's official Drive SDK. Your notes are end-to-end encrypted by the SDK before they leave the device, with the same implementation Proton's own clients use.
[!WARNING] This plugin is vibe-coded. It was written largely by an AI coding assistant, with a human directing and reviewing the work rather than writing every line. It has automated tests, but it has not been through a security audit or long real-world use, and it syncs, and can delete, the files in your vault. Try it on a copy of a vault first, keep backups, and expect bugs.
Status: alpha, and it depends on a pre-release SDK. Read Before you rely on this first. Keep a backup.
.obsidian settings.-, lines only in the other in green with +, and the changed
words highlighted. Then pick which version to keep from the same window.A few things are worth knowing before you point this at a vault you care about.
"Almost instant" is one-way. Local edits upload within about two seconds of you stopping typing. Changes made on another device take up to the poll interval to arrive — 30 seconds by default. The Proton Drive API has no push or websocket channel, so there is no way to do better than polling, and Proton's usage guidelines ask third-party clients not to poll aggressively: an account that does can be rate-limited. You can lower the interval in settings, down to a floor of 15 seconds, at your own risk. This is a limit of the service, not of this plugin.
The SDK is not released for third-party use yet. Proton's README allows personal, non-commercial projects like this one and asks that they go through the SDK rather than the raw API — which is what this does — but it also says the interface may still change, and it describes a cryptographic model change targeted for late 2026 / early 2027 after which clients that have not been updated will stop interoperating. If this plugin is not updated by then, it will stop working.
Desktop and mobile. The plugin runs on Windows, macOS, Linux, Android and
iOS. Requests go out through Obsidian's requestUrl on every platform, and
sign-in and encryption use only web APIs. Differences on mobile:
Mobile support is new and has had far less testing than desktop.
Encryption runs on the UI thread. The SDK encrypts and decrypts in-process, so a very large attachment can make Obsidian stutter while it transfers. On desktop, files over 32 MB are streamed to and from disk rather than held in memory, so size is not a hard limit, but use Skip files larger than in settings if the stutter bites.
Requires Obsidian 1.13.7 or later.
There is no community-plugin listing. Build it and copy it in:
npm install
npm run build
Then copy main.js, manifest.json and styles.css into
<your vault>/.obsidian/plugins/proton-drive-sync/, and enable the plugin in
Settings → Community plugins.
On Android and iOS, put the same three files in that folder of the vault on the device, using a file manager, a USB cable, or a plugin installer such as BRAT.
For development, npm run dev rebuilds on change; point it at a test vault by
building into that vault's plugin folder.
The first time the plugin loads, a setup window walks you through it. It only appears once. To see it again, run Open setup assistant from the command palette. Everything it sets is also in Settings → Proton Drive Sync.
If both the vault and the Drive folder already hold files, the first sync shows a preview first: how many files will be downloaded, uploaded, or are on both sides with different content, with the file names one click away. Start syncing goes ahead; Not now pauses syncing until you resume it. A first sync never deletes anything on either side. The same preview appears after Rebuild sync state or a change of Drive folder, which are first syncs too.
On each additional device, sign in and pick the same folder. The first sync pairs up files that already match, byte for byte, without transferring them. Settings from Drive take precedence over a new device's defaults. Restart Obsidian after that first sync so it loads them.
A conflict is when a file changed on both sides since they last agreed. An edit on one side only is not a conflict; it is just a sync.
| Setting | What happens |
|---|---|
| Keep both versions (default) | This device's version keeps its filename. The other is saved beside it as note (conflict 2026-09-18 1431 from laptop).md. Both then sync everywhere. |
| Merge the changes | Combines edits to different parts of a note — the usual shape after a device has been offline. Falls back to keeping both when the edits overlap, when the file is not text, or when the previous version is no longer in Drive's revision history. |
| Keep whichever was edited last | Uses modification times. Falls back to keeping both when they tie, or when Drive has no recorded time for the file. |
| Keep this device's / Keep Drive | The chosen side keeps the filename; the other is kept as a conflict copy unless you turn copies off. |
| Ask me each time | Nothing is written. The file is skipped until you choose, via Show sync conflicts in the command palette or the status bar's right-click menu. |
Under Ask me each time, each file in Show sync conflicts has a
Compare button. It shows this device's version against the one on Drive as
a diff: lines only on this device in red with -, lines only on Drive in green
with +, a few unchanged lines around each change, and the rest folded away.
The Markdown is shown as source, so a changed link target or heading level is as
visible as a changed word. Keep both, Keep this device and Keep Drive
are right underneath.
Under the other policies, a conflict usually leaves a conflict copy beside the note. Open either file and run Compare with conflict copy from the command palette, or click Compare in the conflict notice. From the diff you can keep the note and delete the copy, or replace the note with the copy. The copy goes to Obsidian's trash either way, so a wrong choice can be undone.
Two cases ignore the setting, because there is no second version to choose between: if a file was deleted on one device and edited on the other, the edit always wins. A deletion can be repeated; a lost edit cannot be recovered.
Deletions that aren't contested do propagate, and locally they go to the system trash rather than being erased. A file is only removed from the vault when Drive confirms it was deleted or trashed. A file that is merely missing from the Drive folder — moved elsewhere in Drive, or not found because a request failed — is kept, and uploaded again if needed.
Files in the .obsidian folder never get conflict copies, since Obsidian would
never read them. When a new device joins, Drive's copy wins. Otherwise the most
recent edit wins.
Click the status bar item to sync now, or to resume when paused. Right-click it for Pause syncing, Show sync conflicts and the settings. The same actions are in the command palette, which is the only way on mobile, where Obsidian has no status bar.
While paused, nothing is uploaded, downloaded or polled. Edits made in the meantime, on this device or elsewhere, are found by the full sync that runs when you resume. The pause is remembered across restarts.
While a sync runs, the status bar shows how many files of the current pass have
been checked (Syncing 120/4000), or the progress of a large transfer
(↑ lecture.mp4 45%). When idle, it says how long ago the last sync finished.
If something goes wrong, Copy sync log (in the command palette, or under Recent activity in settings) copies the recent log with the plugin and Obsidian versions, ready to paste into a bug report. It includes file names, so look it over before sharing it.
Regardless of settings: .obsidian/workspace.json and the other pane-layout and
cache files (devices fight over them), this plugin's own sign-in, sync state and
settings (they belong to each device), .trash/, .git/, .DS_Store,
Thumbs.db, and editor scratch files. .obsidian as a whole is excluded if you
turn off Sync Obsidian settings.
Add your own exclusions as globs — Private/, **/*.pdf — in settings. A
pattern that would not do what it looks like, such as one with a leading / or
Windows \ separators, is flagged under the field as you type.
Each device keeps its own record of the last version it agreed on with Drive, so devices never need to be online together. What to expect when they are:
Note.md and note.md, can
exist side by side on Drive, Linux and Android. They are the same file on
Windows, macOS and iOS. Such pairs are left alone, with a warning in the
plugin's log, until you rename one of them.vault events ─┐ ┌─ Drive events (polled)
├─→ batched ─→ reconcile() ─→ apply ┤
sync state ───┘ │ └─ upload / download
└─→ conflict ─→ policy ─→ merge / copy
src/sync/reconcile.ts is the core: a pure function from
(last-agreed version, local file, remote node) to one decision. It does all the
interesting thinking and none of the I/O, which is why the awkward cases have
tests rather than anecdotes.
| Path | What lives there |
|---|---|
src/sync/reconcile.ts |
The decision table. Pure. |
src/sync/merge.ts |
Line-level three-way merge. Pure. |
src/sync/engine.ts |
Watches both sides and applies decisions. |
src/sync/state.ts |
What the last sync agreed on, per path. |
src/proton/ |
SDK wiring: transport, session, credentials. |
src/proton/account/ |
Vendored from Proton's SDK repo — see its VENDORED.md. |
Run the tests with npm test and the Obsidian review rules with npm run lint;
CI runs both. The tests cover the reconciliation table, the merge, the diff,
path filtering, the state store and the HTTP transport — everything that can be
exercised without a real vault and a real Proton account.
The Proton session — access token, refresh token, and the password that unlocks
your keys — is kept in Obsidian's secret storage. That storage belongs to the
device, not the vault, and uses the platform's keystore where there is one. It
is never written inside the vault, so it is never synced, and a copied vault
does not carry your sign-in with it. Each device signs in on its own. Sessions
saved by version 0.1.0 in session.json are moved into secret storage on the
first launch, and the file is deleted.
The sync state in sync-state.json holds paths, node ids and content hashes. No
file contents, and no key material.
Obsidian's plugin review flags some of what this plugin does. Here is each one and why it is there.
*.proton.me) and nothing
else, through Obsidian's requestUrl. File contents and names are encrypted
on the device before they leave it.fs and hashed with Node's crypto
instead of being read whole into memory, and downloads are written to a
temporary file beside the target and renamed into place once complete. Only
paths inside the vault are opened, resolved through the adapter's own
getFullPath. On mobile, where there is no Node, everything goes through the
adapter and large files are read whole.src/proton/telemetry.ts).Function / new Function). Not in the plugin's own code.
It comes from two bundled libraries: ttag, the translation library inside
Proton's Drive SDK, which compiles plural-form rules, and core-js, whose
polyfills use it for feature detection (Function("return this") and an
async-generator probe). None of it runs on file contents or on anything
received from the network.Copyright (C) 2026 MarckFp.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE for the full text.
GPL-3.0 because the built plugin bundles @protontech/crypto, which Proton
publishes under GPL-3.0, so the main.js users install has to be distributed
under the GPL anyway. Other bundled packages keep their own licences and are
listed in a header at the top of main.js, among them Proton's OpenPGP.js fork
(LGPL-3.0+) and several MIT and BSD packages, all compatible with the GPL.
src/proton/account/ is vendored from
ProtonDriveApps/sdk and stays under
its original MIT licence (© Proton AG); see src/proton/account/LICENSE.md and
src/proton/account/VENDORED.md.
Not affiliated with or endorsed by Proton AG.