Skip to content

Repository files navigation

Proton Drive Sync

Proton Drive Sync

Two-way file sync between a local folder and Proton Drive — for Linux.

A fast Rust daemon, a scriptable control CLI, and a desktop app that shows you every plan before it runs.

📖 Read the full documentation →

The Proton Drive Sync desktop app mid-sync: a hexagon showing three pending changes, the headline "Syncing 3 changes", and a live transfer queue naming each file and its direction.

The desktop app during a sync pass. See every screen →


Caution

This is an early prototype — test with disposable folders and backups first. Sync is bidirectional and deletions propagate: removing a synced file on one side removes it from the other. A file or folder deleted on Proton Drive is deleted from your local disk permanently — not moved to your OS trash — and folder deletions remove the whole subtree. Always preview with --dry-run and check destructive_actions before running unattended. (The very first sync of an existing remote is a non-destructive merge — nothing is deleted.) See Safety below.

What it is

Proton Drive Sync keeps a local folder and a Proton Drive folder in sync, both ways. It ships as three pieces backed by one Rust engine:

Piece Binary What it is
Daemon proton-syncd A background service that watches a local folder and reconciles it with Proton Drive. Also hosts the one-shot --dry-run planner.
Control CLI proton-sync A thin client over a Unix socket — status, pause, resume, syncnow, history, and delete approvals.
Desktop app proton-sync-gui A Tauri app: live status, activity, conflict resolution, dry-run review, settings, onboarding, and a tray icon.

It uses Proton's own proton-drive CLI for every remote file operation (list, upload, download, delete), and reads Proton's volume-event stream directly over HTTPS — authenticated by reusing the CLI's login session rather than any credentials of its own. Either way, proton-drive must be installed, authenticated, and on your PATH first.

How it decides what to do

Every reconcile compares three sources of truth — the local files, the remote files, and the last-synced baseline in a local SQLite index — then plans and executes actions (upload, download, move, conflict, delete, …). Comparing all three, not just the two live sides, is what lets it tell a new file from a deleted one and an edit from a move. Each index change is committed only after its side effect succeeds, in incremental checkpoints as work completes, so a failure mid-plan keeps completed work durable and never leaves half-recorded state. → How sync works

Quick setup

One command installs the engine, writes the config a background service reads, and starts syncing. You need a Rust toolchain (edition 2024 / Rust ≥ 1.85) and an authenticated proton-drive CLI on your PATH first — the engine drives it for every remote operation:

proton-drive filesystem list --json /Drive/RemoteFolder   # confirm the CLI works & is logged in
git clone https://github.com/osirison/proton-drive-sync-engine.git
cd proton-drive-sync-engine

Terminal — pass the folders on the command line:

./setup.sh --local-root ~/ProtonDrive --remote-root /Drive/RemoteFolder

This builds and installs proton-syncd + proton-sync, writes ~/.config/proton-sync/proton-sync.toml, installs a systemd user service that runs proton-syncd --config <that file>, previews a dry-run (showing the destructive_actions count as a safety check), and — once you confirm — enables and starts the service.

Desktop — pick the folders in a wizard, no terminal after launch:

./setup.sh --gui

This also builds the desktop app and opens it straight into the onboarding wizard: check the CLI, choose the folder pair, review the first dry-run, and press Start. The wizard writes the same config the service reads, so the two never disagree.

Once it's running, drive it however you like:

proton-sync status                        # what is the daemon doing?
systemctl --user status proton-syncd      # the service itself

./setup.sh --help lists every flag (--no-start, --force-config, --proton-cli, --no-build, …). The next section explains what the script does, step by step.

Upgrade and uninstall

Two companion scripts sit beside setup.sh and reuse its installer logic, so the systemd unit and desktop launcher they write never drift from what a fresh install would produce.

Upgrade an existing install — rebuild the binaries from this checkout, refresh the unit and (if installed) the desktop launcher, then restart the running service. Your config, folder pairing, and sync state are left untouched:

git pull                 # or: ./upgrade.sh --pull  (fast-forwards for you)
./upgrade.sh             # rebuilds whatever is installed, then restarts the service

It auto-detects whether the desktop app is installed and upgrades it too; scope it with --engine-only / --gui, skip the rebuild with --no-build, or leave the service stopped with --no-restart. It refuses to run on a machine with nothing installed (use setup.sh for a first install).

Behaviour change on this upgrade — local deletions now go to your desktop's Trash. When Proton Drive says a file was deleted, the engine used to remove your local copy from disk with no way back. It now moves it to the system Trash (~/.local/share/Trash, the FreeDesktop location), so any file manager can restore it to its original path. Your approval settings are untouched: deletion_policy still decides whether a deletion waits for you, and this only changes what happens once it goes ahead. Add local_delete_mode = "permanent" to your config — or pick Delete them permanently on the app's Settings → Deletions tab — to restore the previous behaviour exactly.

Uninstall — remove the service, binaries, config, desktop app files, and all engine state, leaving the machine clean. Preview first; it prompts once before deleting:

./uninstall.sh --dry-run    # print exactly what would be removed, change nothing
./uninstall.sh              # remove everything (or -y to skip the prompt)

It never touches the files you were syncing (only the .sync state dir inside your local root), nor the separate proton-drive CLI and its login. Pass --keep-config to preserve the config and folder pairing for a later reinstall. ./uninstall.sh --help lists every flag.

Manual setup — what the script does

Prefer to run each step yourself, or skip systemd and just run the daemon in the foreground? setup.sh automates essentially these steps (it also resolves the exact binary path for the unit's ExecStart, previews a dry-run as a safety gate, and — with --gui — hands folder selection to the desktop onboarding wizard):

1. Confirm the proton-drive CLI works — the daemon fails the same way if it doesn't:

proton-drive filesystem list --json /Drive/RemoteFolder

2. Build and install the binaries to $CARGO_HOME/bin (defaults to ~/.cargo/bin):

cargo install --path .            # installs proton-syncd + proton-sync

(Or cargo build --release --bins and add target/release to your PATH for a throwaway run.)

3. Preview the plan — this touches nothing; it prints exactly what would happen. Replace the paths with yours; the local folder must exist first (mkdir -p ~/ProtonDrive), the remote folder is created on the first real sync. Check destructive_actions before going further:

proton-syncd --local-root ~/ProtonDrive --remote-root /Drive/RemoteFolder --dry-run

4a. Run it as a systemd user service (this is what the script automates) — write a config, install the unit, and enable it. The heredoc is unquoted so $HOME expands to a real absolute path (systemd/TOML don't expand ~):

mkdir -p ~/.config/proton-sync ~/ProtonDrive
cat > ~/.config/proton-sync/proton-sync.toml <<TOML
local_root  = "$HOME/ProtonDrive"
remote_root = "/Drive/RemoteFolder"
# local_delete_mode = "trash"      # default. "permanent" deletes from disk instead.
TOML
# The sample unit's ExecStart is %h/.cargo/bin/proton-syncd — correct if step 2 installed to the
# default ~/.cargo/bin; edit it if your CARGO_HOME/CARGO_INSTALL_ROOT differs.
install -Dm644 examples/systemd/proton-syncd.service \
  ~/.config/systemd/user/proton-syncd.service
systemctl --user daemon-reload
systemctl --user enable --now proton-syncd

4b. …or just run the daemon in the foreground (no systemd):

proton-syncd --local-root ~/ProtonDrive --remote-root /Drive/RemoteFolder

5. Drive it from another terminal:

proton-sync status      # is it running? what did it just do?
proton-sync syncnow     # reconcile now
proton-sync pause       # / resume
proton-sync history     # recorded passes: how long, what kind, how they ended
proton-sync activity    # what moved recently (or one path's own history)

State (the SQLite index and its sidecars) lives in <local-root>/.sync/, which is always ignored by sync. The control socket is at $XDG_RUNTIME_DIR/proton-sync.sock.

Quick start guide · Running as a service · Daemon reference · CLI reference

The desktop app

Prefer not to touch a terminal? The quickest path is ./setup.sh --gui (see Quick setup), which builds the app, installs a launcher, and opens the onboarding wizard for you. To build and run it by hand from a checkout instead:

1. Install system dependencies (Linux/Fedora; adjust for your distro):

sudo dnf install webkit2gtk4.1-devel libsoup3-devel gtk3-devel libappindicator-gtk3-devel librsvg2-devel gtk-layer-shell-devel

2. Start the daemon (from the project root):

cargo run --bin proton-syncd -- --local-root /tmp/demo --remote-root /Drive/RemoteFolder

3. In another terminal, run the GUI:

cargo run -p proton-sync-gui

The desktop app is a thin client over the same daemon — it owns no sync logic and reads the same socket and files as the CLI, so the two never disagree. It gives you:

  • An onboarding wizard — check the CLI, choose the folder pair, review the first dry-run, and start the service.
  • Live status with an activity ledger and per-state actions.
  • Side-by-side conflict resolution — compare your version against Proton's and pick Keep mine / Use Proton's / Keep both, staged until you Apply.
  • A plan preview that gates a destructive apply behind a typed DELETE confirmation.
  • A Deletions queue for approving withheld deletes, and Settings that edit your config file safely.
  • A system-tray indicator with five states; closing the window hides it to the tray and syncing keeps running.

The plan screen: the heading "The next sync moves 9 things", counts for what leaves this computer and what arrives from Proton, a red band warning that one file gets deleted for good, a list of every action with an arrow for its direction and what becomes of it, and a DELETE box that must be typed into before the plan can run.

The plan screen — a rehearsal of the next sync. Nothing has changed yet, and the destructive row has to be armed by typing DELETE before it can run.

Desktop app docs · Every screen, with pictures · Native packages (RPM / deb / AUR)

Safety (read this)

Two-way sync means deletions and overwrites really happen. The engine is built to make that safe and predictable.

  • Deletions propagate, and both sides are recoverable by default. Deleting a file locally moves the Proton Drive copy to Proton's Trash. Deleting a file or folder on Proton Drive moves your local copy to your desktop's Trash — restorable from Files, Dolphin or any file manager, to its original path. Set local_delete_mode = "permanent" to get the old behaviour back: a direct filesystem delete, recursive for folders, with no local-side undo.
  • The delete/edit safeguard. A deletion only propagates if the other side hasn't changed since the last sync. If you delete a file locally but it was edited on Proton Drive, the edit is restored instead — the surviving edit always beats the delete.
  • Delete approval is on by default. A directional guard withholds destructive deletes (per direction, per item) until you approve them via proton-sync pending / approve or the app's Deletions screen. Relax it per folder with a .proton-sync.toml, or globally with --no-delete-approval.
  • Conflicts keep both sides. When both sides change differently, the engine keeps your local file and writes the remote version to a .proton-cloud sidecar — nothing is lost. Resolve by deleting the sidecar (keep yours) or moving it over the original (adopt Proton's).
  • Preview anything with --dry-run and watch destructive_actions.

Deletions & the safeguard · Delete approval · Conflicts

More features

  • Selective sync — include/exclude globs applied to local files, remote files, and the index, so excluded paths are never treated as deleted. → Selective sync
  • Fast incremental reconcile — Proton's volume-event stream gives O(changes) change detection (on by default), with automatic full-scan fallback when the stream can't resolve a change; a periodic full scan is an opt-in safety net (off by default). → Change detection
  • Config files with --config, plus hierarchical per-directory .proton-sync.toml. → Configuration
  • Run as a systemd user service — set up in one command by setup.sh, or by hand with the sample units and install helper. → Running as a service
  • Rename/move detection for files in either direction and for remote-side directories; structured tracing logs; file-manager emblems for Nautilus and Nemo.

Unix-only today (control-plane IPC uses Unix domain sockets); Linux is the primary target.

Documentation

Full docs — installation, concepts, safety, complete daemon/CLI/UI reference, packaging, and troubleshooting — live at:

➡️ https://osirison.github.io/proton-drive-sync-engine/

The site is built from website/ with Astro + Starlight and deploys to GitHub Pages on every push to main. Architecture and design records are in docs/ (see the ADRs).

Project layout

setup.sh        One-command installer (terminal quick setup, or --gui onboarding)
upgrade.sh      In-place upgrade of an existing install (rebuild + refresh unit/launcher + restart)
uninstall.sh    Remove everything cleanly (keeps your synced files and the proton-drive CLI)
src/            The engine (library crate) + the two binaries
  bin/          proton-sync.rs (CLI) · proton-syncd.rs (daemon)
  sync.rs       Pure planner: the reconcile decision matrix + conflict naming
  daemon.rs     Runtime loop, watcher, IPC, reconcile execution + commit
  events.rs     Remote change detection via Proton's volume-event stream
  reconstruct.rs  base ⊕ delta remote-map reconstruction (event-driven)
  index.rs      SQLite schema, scanning, hashing, cursor, delete approvals
  proton.rs     proton-drive CLI wrapper + remote JSON parser
  config.rs     Layered config resolution · dirconfig.rs  per-directory settings
gui/            The desktop app (Tauri)
  gui-core/     Pure-Rust data layer (the typed boundary to the daemon)
  src/          Vanilla-JS webview frontend · src-tauri/  the Tauri shell
  package.json  eslint + prettier for src/ (dev-only; nothing here is bundled)
website/        This documentation site (Astro + Starlight)
packaging/      RPM · deb · AUR · freedesktop · file-manager emblems
examples/       Sample config, systemd unit, release-archive helper
docs/           Design docs + ADRs

Development

Run the validation suite before opening a PR — CI enforces it:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-targets --all-features

--workspace is required on clippy/test: the workspace root is itself a package, so Cargo's default member selection is just that root crate and the gui/ members would go unlinted and untested. (cargo fmt --all already covers every member.)

The desktop app's webview frontend (gui/src, vanilla ES modules with no bundler) has its own eslint + prettier gate, also enforced by CI:

cd gui
npm ci        # first time, and after any gui/package-lock.json change
npm run check # prettier --check, then eslint

The planner (src/sync.rs) is pure and tested directly; the daemon is generic over its Proton client so reconciliation is tested with injected fakes. Add regression tests next to the logic you change. → Development guide

License

Licensed under the terms in LICENSE (Apache-2.0).

About

Two-way file sync between a local folder and Proton Drive on Linux — a fast Rust daemon, a scriptable control CLI, and a Tauri desktop app that previews every change before it runs.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages