mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
docs: syncing several computers into one device; changelog for 3.5.0
Add B248 to B252 to the bug ledger, the shared-device feature and schema 44 to the 3.5.0 changelog, and describe the join flow, per-machine profiles, conflict copy names and the mirror's multi-writer rules in the user guide, recovery guide, CLI and configuration references and the architecture notes.
This commit is contained in:
1 parent
7a0f1ebd6f
commit
11d03091a7
7 files changed
+204
-12
No files matched your search
+26
-2
@@ -255,7 +255,9 @@ not one this daemon sealed itself (`own_sealed_rev` table), the queued bytes are
|
||||
conflict copy (`Core::revision_conflict`) next to the remote version. A name taken on Drive by a
|
||||
node this machine has not seen lands under a conflict name too. Both go to the activity log.
|
||||
|
||||
Conflict copies are named `{stem} (sync-conflict {unix_secs}).{ext}`.
|
||||
Conflict copies are named `{stem} (sync-conflict {host} {unix_secs}).{ext}`, where `{host}` is the
|
||||
hostname cut down to one token (`conflict_label`). `conflict_base_name` also reads the older form
|
||||
without a host.
|
||||
|
||||
### Failures and sync issues
|
||||
|
||||
@@ -342,6 +344,22 @@ Safety rules:
|
||||
- **Ignore rules.** `.pdfsignore` (or `.protonignore`) at the folder root plus `ignore_patterns`
|
||||
from `config.json`, in gitignore syntax. Ignoring never deletes.
|
||||
|
||||
Another client may write the same folder, such as a second computer syncing into the device. The
|
||||
baseline therefore also records the active revision id each file was synced at (schema 44):
|
||||
|
||||
- **A new revision is a remote change** even when the link's modification time and the size come
|
||||
out the same.
|
||||
- **An upload checks first.** `upload_revision` reads the node and leaves a file whose revision
|
||||
moved since the walk for the next pass, which classifies it as a conflict. A revision committed
|
||||
between that check and the SDK's own revision draft is not caught.
|
||||
- **The baseline records what was transferred.** A download records the revision the pass
|
||||
planned to fetch, not a read-back that may already show a newer one. An upload records the
|
||||
read-back only when its SHA-1 is the one sent.
|
||||
- **A first sync compares content.** A path present on both sides without a baseline is adopted
|
||||
only when the local SHA-1 matches Drive's digest; otherwise it is a conflict.
|
||||
|
||||
Remote changes still reach a mirror only through the 120 s poll.
|
||||
|
||||
### Moves between locations
|
||||
|
||||
My files and each online-only folder are separate FUSE sessions, and a mirrored folder has none,
|
||||
@@ -369,12 +387,18 @@ named like the hostname, else a new one. An adopted uid that no longer exists is
|
||||
replaced silently.
|
||||
|
||||
The local database does not survive a lost machine, so the daemon backs up its arrangement to
|
||||
`<device root>/.proton-drive-linux/profile.json` (`pdfs_core::profile`): each synced folder's
|
||||
`<device root>/.proton-drive-linux/profile-<machine id>.json` (`pdfs_core::profile`): each synced folder's
|
||||
remote uid, local path and mode, the pins, and the ignore patterns. It is uploaded like any other
|
||||
file, so it is end-to-end encrypted. `Core::touch_profile` coalesces bursts of changes into one
|
||||
upload. `PROFILE_VERSION` guards the format: a profile from a newer client is refused, not applied
|
||||
in part.
|
||||
|
||||
The machine id is random and lives in `machine-id` next to `config.json`. Each machine has its
|
||||
own file because several computers may sync into one device: `pdfs devices join` and **Sync With
|
||||
This Computer…** adopt another computer's device without taking it over. Restore reads every
|
||||
profile there, the legacy shared `profile.json` included, and prefers the one with this hostname,
|
||||
then the newest (`profile::saved_folder`).
|
||||
|
||||
Restore lists the device's folders, proposes a local path from the profile, and on confirmation
|
||||
adds a synced folder bound to the remote uid. The download is an ordinary pass against an empty
|
||||
baseline. The user side is in [RECOVERY.md](RECOVERY.md).
|
||||
|
||||
+125
@@ -100,6 +100,11 @@ a live account since.
|
||||
|
||||
| Entry | Title |
|
||||
|---|---|
|
||||
| [B252](#b252--computers-sharing-a-device-overwrite-each-others-profile) | Computers sharing a device overwrite each other's profile |
|
||||
| [B251](#b251--two-computers-can-make-the-same-conflict-copy-name) | Two computers can make the same conflict copy name |
|
||||
| [B250](#b250--a-revision-made-during-a-mirror-download-never-arrives) | A revision made during a mirror download never arrives |
|
||||
| [B249](#b249--a-mirror-edit-replaces-a-revision-another-computer-made-since) | A mirror edit replaces a revision another computer made since |
|
||||
| [B248](#b248--a-joining-mirror-takes-a-same-size-file-for-the-same-file) | A joining mirror takes a same-size file for the same file |
|
||||
| [B247](#b247--tray-status-lines-are-not-translated) | Tray status lines are not translated |
|
||||
| [B246](#b246--signing-in-with-a-locked-keyring-freezes-the-app-window) | Signing in with a locked keyring freezes the app window |
|
||||
| [B245](#b245--any-child-of-the-daemon-can-answer-the-watchdog-for-it) | Any child of the daemon can answer the watchdog for it |
|
||||
@@ -350,6 +355,126 @@ a live account since.
|
||||
|
||||
Newest first.
|
||||
|
||||
### B252 — Computers sharing a device overwrite each other's profile
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-16, design review for syncing several computers into one device.
|
||||
**Where:** `save_profile` and `load_profiles` in `crates/pdfs-fuse/src/profile.rs`
|
||||
|
||||
**Repro:** Adopt one device on two computers and sync a different folder on each. The
|
||||
device's `profile.json` lists only the folders of whichever computer saved last.
|
||||
|
||||
**Cause.** The profile was one file per device, written as a new revision by every computer
|
||||
that synced into it, each with only its own folders.
|
||||
|
||||
**Fix.** Each machine writes `profile-<machine id>.json`. The id is random, made on first use
|
||||
and kept in `machine-id` next to `config.json`, so nothing that identifies the host goes to
|
||||
Drive. Restore reads every profile in the folder, including the shared `profile.json` of older
|
||||
clients. A folder's proposal comes from this computer's own profile, otherwise from the newest
|
||||
one that has the folder. The legacy file is left alone.
|
||||
|
||||
**Test:** `the_saved_folder_comes_from_this_computer_then_the_newest_profile` and
|
||||
`a_profile_name_is_the_legacy_one_or_one_per_machine` in `pdfs-core`;
|
||||
`each_machine_saves_its_own_profile` in `pdfs-fuse`.
|
||||
|
||||
---
|
||||
|
||||
### B251 — Two computers can make the same conflict copy name
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-16, design review for syncing several computers into one device.
|
||||
**Where:** `conflict_path_with_suffix` in `crates/pdfs-fuse/src/sync/planner.rs`, `conflict_name`
|
||||
in `crates/pdfs-fuse/src/lib.rs`
|
||||
|
||||
**Repro:** Two computers sync one folder and each keeps a conflict copy of the same file in the
|
||||
same second. Both name it `notes (sync-conflict 1700000000).txt`, so the second upload meets
|
||||
the first on Drive, and neither copy says which computer it came from.
|
||||
|
||||
**Cause.** The name held only the time. The `-<n>` suffix is made unique on the local disk, not
|
||||
on Drive.
|
||||
|
||||
**Fix.** Conflict copies carry the hostname, cut down to one token of letters, digits, `-`, `_`
|
||||
and `.`: `notes (sync-conflict laptop 1700000000).txt`. `conflict_base_name` reads both forms,
|
||||
so the sweep and **Resolve** still find copies made before.
|
||||
|
||||
**Test:** `a_hostname_becomes_one_safe_token`, `conflict_name_keeps_the_extension`,
|
||||
`conflict_path_keeps_extension_and_directory` and `parses_conflict_names_and_leaves_others_alone`.
|
||||
|
||||
---
|
||||
|
||||
### B250 — A revision made during a mirror download never arrives
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-16, design review for syncing several computers into one device.
|
||||
**Where:** `download_file` and `record_file_baseline` in `crates/pdfs-fuse/src/sync.rs`
|
||||
|
||||
**Repro:** Another computer saves a file in a mirrored folder while this computer downloads the
|
||||
previous version. This computer keeps the previous version, and later passes do not fetch the
|
||||
newer one.
|
||||
|
||||
**Cause.** After a download the baseline read the node again and recorded what Drive held then.
|
||||
A revision committed while the download ran was recorded as synced, although the disk held the
|
||||
older one.
|
||||
|
||||
**Fix.** The baseline records the revision the pass planned to download. If the download already
|
||||
fetched the newer one, the next pass fetches it once more, which costs a transfer and loses
|
||||
nothing. After an upload the read-back is still used, but only when Drive's digest is the one
|
||||
just sent; otherwise the baseline is left as it was and the next pass keeps a conflict copy.
|
||||
|
||||
**Test:** `a_revision_made_during_a_mirror_download_still_arrives` (simulation, FUSE). It fails
|
||||
with the old read-back.
|
||||
|
||||
---
|
||||
|
||||
### B249 — A mirror edit replaces a revision another computer made since
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-16, design review for syncing several computers into one device.
|
||||
**Where:** `upload_revision` in `crates/pdfs-fuse/src/sync.rs`
|
||||
|
||||
**Repro:** Edit a file in a mirrored folder while another computer saves its own edit of the
|
||||
same file, after this computer's pass looked at Drive and before it uploads. This computer's
|
||||
revision becomes the current one. The other edit survives only in the version history, and no
|
||||
conflict copy is made.
|
||||
|
||||
**Cause.** The upload made a new revision of whatever Drive held at that moment, from a plan
|
||||
that could be minutes old. Nothing compared Drive with the baseline before uploading. Change
|
||||
detection used only the link's modification time and the size.
|
||||
|
||||
**Fix.** The baseline records the revision id it was synced at (schema 44). The upload reads
|
||||
the node first and leaves a file whose revision moved for the next pass, which keeps the local
|
||||
edit as a conflict copy. A new revision id counts as a remote change even at the same
|
||||
modification time and size. A revision committed between the check and the upload's own draft
|
||||
is not caught, because the SDK reads the current revision itself; the read-back after the upload
|
||||
catches one committed after it.
|
||||
|
||||
**Test:** `a_mirror_edit_does_not_replace_a_revision_another_computer_made_since` (simulation,
|
||||
FUSE), `a_new_remote_revision_is_a_change_even_at_the_same_mtime_and_size` and
|
||||
`migration_v44_adds_the_synced_revision_without_disturbing_a_v43_baseline`.
|
||||
|
||||
---
|
||||
|
||||
### B248 — A joining mirror takes a same-size file for the same file
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-16, design review for syncing several computers into one device.
|
||||
**Where:** `plan_file` in `crates/pdfs-fuse/src/sync/planner.rs`
|
||||
|
||||
**Repro:** Sync a local folder into a folder on Drive that already holds a file of the same name
|
||||
and size but different content, for example on a second computer joining a folder. The first
|
||||
pass records both as in step, and the two computers go on holding different files.
|
||||
|
||||
**Cause.** The first sync of a path present on both sides adopted the baseline whenever the sizes
|
||||
matched, to avoid a conflict copy of every pre-existing file.
|
||||
|
||||
**Fix.** The adoption needs Drive's content digest, and the local file is hashed first. A match
|
||||
is adopted; a different file, or one without a digest on Drive, is kept as a conflict copy.
|
||||
|
||||
**Test:** `a_joining_mirror_keeps_a_same_size_different_file_as_a_conflict` (simulation, FUSE)
|
||||
and `first_sync_without_a_remote_digest_is_a_conflict`.
|
||||
|
||||
---
|
||||
|
||||
### B247 — Tray status lines are not translated
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
|
||||
+20
-2
@@ -13,10 +13,28 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
|
||||
|
||||
## [3.5.0] — 2026-10-16
|
||||
|
||||
Schema: **43**. Queued changes get an index by kind and name. Nothing is rewritten, and the
|
||||
upgrade takes a moment.
|
||||
Schema: **44**. Queued changes get an index by kind and name, and each synced file remembers the
|
||||
version on Proton Drive it was synced at. Nothing is rewritten, and the upgrade takes a moment.
|
||||
|
||||
### Added
|
||||
- **Several computers can sync the same folders.** On the Computers page, **Sync With This
|
||||
Computer…** on another computer, or `pdfs devices join`, makes this computer sync into that
|
||||
computer's backup alongside it, then offers its folders for syncing here. Both computers keep
|
||||
them in step, deletions included. Each computer now keeps its own settings backup in the
|
||||
shared backup, so they no longer overwrite each other (B252).
|
||||
|
||||
### Fixed
|
||||
- **An edit in a synced folder could replace one another computer had just saved.** The newer
|
||||
version from the other computer was left only in the version history. The edit is now kept as
|
||||
a conflict copy (B249).
|
||||
- **A version saved on another computer while this one downloaded the previous version never
|
||||
arrived.** (B250)
|
||||
- **Syncing a folder into one that already held a file of the same name and size took the two
|
||||
for the same file**, even when their content differed. The content is now compared, and a
|
||||
different file is kept as a conflict copy (B248).
|
||||
- **Conflict copies now name the computer that made them**, for example
|
||||
`notes (sync-conflict laptop 1700000000).txt`, so two computers no longer make the same name
|
||||
(B251).
|
||||
- **A folder deleted on another device took files with it that had never reached Drive.** A new
|
||||
file, an edit or an ignored `.git` in a synced folder was deleted along with the folder. Such a
|
||||
folder is now kept and created on Drive again (B211).
|
||||
|
||||
@@ -102,6 +102,7 @@ commands are for scripts and for cases where the mount is not available.
|
||||
| `pdfs devices list` | List computers registered to the account. |
|
||||
| `pdfs devices rename UID NAME` | Rename a computer. |
|
||||
| `pdfs devices rm UID` | Remove a computer and its backup from the account. |
|
||||
| `pdfs devices join UID [--yes]` | Sync the same folders as another computer. This machine syncs into that computer's backup alongside it, then offers its folders like `pdfs sync restore`. Both computers keep them in step. `--yes` skips the confirmation and accepts every proposed path. |
|
||||
| `pdfs devices adopt UID` | Make this machine continue another computer's backup, for example after a reinstall. `--clear` returns to matching by hostname. |
|
||||
| `pdfs devices migrate UID [--yes]` | Move another computer's folders into this computer's backup, then restore them here like `pdfs sync restore`. The other computer stays registered, without those folders. `--yes` skips the confirmation and accepts every proposed path. See [RECOVERY.md](RECOVERY.md). |
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ and where the client keeps its data.
|
||||
| Path | Contents |
|
||||
|---|---|
|
||||
| `~/.config/proton-drive-linux/config.json` | Settings (this page) |
|
||||
| `~/.config/proton-drive-linux/machine-id` | A random id for this machine. It names this machine's profile in a computer's backup that several computers may share |
|
||||
| `~/.local/state/proton-drive-linux/cache.db` | Metadata database: names, folder tree, sync state, upload queue, photo index |
|
||||
| `~/.local/state/proton-drive-linux/sdk_cache.db` | Encrypted cache of Proton Drive metadata. Safe to delete while the service is stopped |
|
||||
| `~/.local/state/proton-drive-linux/control.sock` | Socket the app, tray, prompt and CLI use to reach the service |
|
||||
@@ -78,7 +79,7 @@ fix it.
|
||||
| `upload_limit` | bytes/s | none | General → Network | Shared cap for all uploads. `0` or `null` means none |
|
||||
| `download_limit` | bytes/s | none | General → Network | Shared cap for all downloads, reads from the mount included |
|
||||
| `ignore_patterns` | list | see below | — | Global ignore rules for synced folders |
|
||||
| `device_uid` | string | none | Computers → Continue This Backup Here… | The computer this machine continues. Unset means "match by hostname" |
|
||||
| `device_uid` | string | none | Computers → Continue This Backup Here… or Sync With This Computer… | The computer this machine syncs into. Unset means "match by hostname" |
|
||||
| `conflict_sweep` | `off` / `report` / `enforce` | `report` | — | See [`conflict_sweep`](#conflict_sweep) |
|
||||
| `local_first` | bool | `true` | — | See [`local_first`](#local_first) |
|
||||
| `language` | gettext code | system | General → Appearance | Language of the app, tray and prompt, such as `de` or `pt_BR` |
|
||||
|
||||
+7
-4
@@ -17,7 +17,7 @@ your synced folders back on the replacement machine.
|
||||
| Every file that finished uploading | Proton Drive |
|
||||
| The computer's registration and its backup | **Computers** in the app and the web |
|
||||
| One folder per synced folder, under that computer | Proton Drive |
|
||||
| The machine profile: which folders were synced, to which local path, in which mode; pins; ignore patterns | `.proton-drive-linux/profile.json` under the computer's backup, end-to-end encrypted like any other file |
|
||||
| The machine profile: which folders were synced, to which local path, in which mode; pins; ignore patterns | `.proton-drive-linux/profile-<machine id>.json` under the computer's backup, one per computer that syncs into it, end-to-end encrypted like any other file. Clients before 3.5.0 wrote `profile.json` |
|
||||
| Your account, keys and shares | Proton |
|
||||
|
||||
The service rewrites the profile a short while after you add, remove or switch a synced folder,
|
||||
@@ -166,18 +166,21 @@ This section is for the curious and for anyone debugging a restore.
|
||||
in the account is reported in the log instead of being replaced silently.
|
||||
- **What can be restored.** `pdfs sync restore` lists the folders directly under the computer's
|
||||
backup, except `.proton-drive-linux`. Folders already synced on this machine are skipped.
|
||||
- **Proposed paths.** When the profile names a local path whose parent directory exists on this
|
||||
- **Proposed paths.** Several computers may sync into one backup, each with its own profile. The
|
||||
profile written by a computer with this machine's hostname is used first, then the newest one
|
||||
that has the folder. When it names a local path whose parent directory exists on this
|
||||
machine, that path is proposed. Otherwise the proposal is `~/<folder name>`. The service never
|
||||
writes to a path you have not confirmed.
|
||||
- **Attaching by UID.** The restore binds each local path to the remote folder's UID, not to its
|
||||
name. Restoring `Documents` into `~/docs` works.
|
||||
- **Downloading.** A restored folder starts with an empty sync baseline. Against an empty local
|
||||
directory, every remote file counts as new and is downloaded. Files present on both sides with
|
||||
no baseline become conflict copies, as described above.
|
||||
no baseline are compared by content: identical files are taken as they are, and different
|
||||
ones become conflict copies, as described above.
|
||||
- **The wipe guard.** A sync pass in which every previously synced file has vanished locally is
|
||||
refused instead of being mirrored to Proton Drive as a mass deletion. A folder that was emptied
|
||||
by accident therefore does not empty the backup.
|
||||
- **The profile format.** `profile.json` carries a `version` field. A client refuses a profile
|
||||
- **The profile format.** Each profile carries a `version` field. A client refuses a profile
|
||||
written by a newer, incompatible client instead of applying part of it.
|
||||
|
||||
## 4. Known gaps
|
||||
|
||||
+23
-3
@@ -188,15 +188,16 @@ Before you shut down a computer for good, check that nothing is waiting: see
|
||||
|
||||
When the same file changed on this computer and somewhere else since the last sync, neither side
|
||||
is thrown away. The local version is kept as a copy named like
|
||||
`report (sync-conflict 1759068180).odt`, where the number is the time of the conflict in seconds
|
||||
since 1970, and the remote version takes the original name.
|
||||
`report (sync-conflict laptop 1759068180).odt`, where `laptop` is the name of the computer that
|
||||
made the copy and the number is the time of the conflict in seconds since 1970. The remote
|
||||
version takes the original name. Copies made before 3.5.0 have no computer name.
|
||||
|
||||
Conflicts appear under **Sync → Overview → Needs attention** and in `pdfs conflicts`. For each
|
||||
one, keep the original, keep the copy, or keep both under a new name:
|
||||
|
||||
```bash
|
||||
pdfs conflicts
|
||||
pdfs conflicts resolve "Documents/report (sync-conflict 1759068180).odt" --keep copy
|
||||
pdfs conflicts resolve "Documents/report (sync-conflict laptop 1759068180).odt" --keep copy
|
||||
```
|
||||
|
||||
The version you do not keep goes to the Trash, so a wrong choice can be undone.
|
||||
@@ -284,12 +285,31 @@ Proton Drive keeps earlier versions of every file. **Details → Versions…** i
|
||||
back folders that are backed up under it but not synced here yet.
|
||||
- **Another computer** can be browsed read-only in the same list or grid as My files, and
|
||||
**Restore to This Computer…** downloads its folders here.
|
||||
- **Sync With This Computer…** makes this computer sync the same folders as another one, for
|
||||
example a desktop and a laptop. Both keep the folders in step: a change or a deletion on one
|
||||
reaches the other. See [Syncing several computers](#syncing-several-computers).
|
||||
- **Continue This Backup Here…** makes this computer take over another computer's backup, for
|
||||
example after reinstalling or replacing a machine. See [RECOVERY.md](RECOVERY.md).
|
||||
- **Move to This Computer…** moves another computer's folders into this computer's backup, for
|
||||
example when you switch from Windows to Linux, and then offers them for syncing here. The other
|
||||
computer stays in the list, without those folders.
|
||||
|
||||
### Syncing several computers
|
||||
|
||||
Two or more computers can sync the same folders through one backup in Proton Drive.
|
||||
|
||||
1. Sync the folders on the first computer as usual.
|
||||
2. On the second computer, open **Computers**, open the menu of the first computer and choose
|
||||
**Sync With This Computer…**. From a terminal: `pdfs devices join UID`, with the UID from
|
||||
`pdfs devices list`.
|
||||
3. Choose which of its folders to sync here and where, as in a restore.
|
||||
|
||||
From then on, both computers sync into that backup. A folder you add later on either computer
|
||||
lands there too, and a folder with the same name joins the one already there. An edit on one
|
||||
computer reaches the other within about two minutes. When both computers change the same file in
|
||||
that time, each keeps the other's version under the original name and its own as a conflict copy
|
||||
named after it. Folders that were synced here before joining stay in this computer's own backup.
|
||||
|
||||
## Search launcher
|
||||
|
||||
`pdfs-prompt` searches Proton Drive and your home folder together, in one ranked list. It
|
||||
|
||||
Reference in new issue
Block a user