mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
Add the window size and the narrow layout to the 3.5.0 changelog with B253, document the `window` config field, and explain in the troubleshooting guide why a two-password account asks for its mailbox password after every reboot, and how to keep it unlocked.
1980 lines
128 KiB
Markdown
1980 lines
128 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to the Linux client since the 1.0.0 release. Format follows
|
||
[Keep a Changelog](https://keepachangelog.com/1.1.0/); versions follow semver, and every
|
||
release is tagged `vX.Y.Z` with `Cargo.toml` and `packaging/PKGBUILD` in agreement.
|
||
|
||
`Schema:` lines record the SQLite `SCHEMA_VERSION` (`pdfs-core/src/db/migrations.rs`) a
|
||
release ships. Migrations are forward-only — a database written by a newer client is a hard
|
||
refuse-to-open, not a downgrade, so rolling back a release means restoring the cache from
|
||
scratch (user data in `staging/` and `recovery/` is never touched by this).
|
||
|
||
## [Unreleased]
|
||
|
||
## [3.5.0] — 2026-10-16
|
||
|
||
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).
|
||
- **The app remembers its window size**, and whether it was maximized, between runs.
|
||
|
||
### Fixed
|
||
- **The app's window could not shrink to fit a small or scaled screen**, so the minimize,
|
||
maximize and close buttons ended up off-screen. Below about 760 pixels the sidebar and the
|
||
page now take turns, and every page fits in a window 480 pixels wide.
|
||
- **The app showed the sign-in page when the system keyring was locked or not running yet**, as
|
||
if the sign-in had been lost. It now says that the keyring could not be read, and opens on its
|
||
own once the keyring answers (B253).
|
||
- **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).
|
||
- **Files on Drive that could not be decrypted were deleted from the synced folder.** They read
|
||
as deleted on Drive. They are now left alone and named in the activity feed, and a pass in which
|
||
nothing synced is left on Drive is refused (B212).
|
||
- **An edit made while a synced folder was being checked could be deleted** when another device
|
||
had deleted the file. It is now kept and uploaded (B213).
|
||
- **Deleting a synced folder sent another device's new files in it to the trash.** The folder now
|
||
stays on Drive with them (B214).
|
||
- **A file name on Drive containing `/` or `..` could write outside the synced folder**, and made
|
||
its folder in the mount fail to list. Such names are left out (B215).
|
||
- **A damaged database lost the changes still waiting to upload.** It is still moved aside and
|
||
rebuilt, but queued changes, synced folders' state and files kept offline are copied out of it
|
||
first (B216).
|
||
- **Search slowed down over time, and the database grew far larger than needed.** The search
|
||
indexes are now compacted once a day; on one machine that took the local index from 317 MiB to
|
||
20 MiB (B217).
|
||
- **`pdfs diagnose` and `pdfs cache inspect` froze the drive while they checked the database**,
|
||
for half a minute on a large one. They no longer hold up anything else (B218).
|
||
- **Listing a folder shared with you could freeze the whole drive on a slow connection**, and so
|
||
could folders of photos asking for thumbnails while the connection stalled (B219, B220).
|
||
- **Opening a large folder for the first time held up every other file operation on the drive.**
|
||
(B221)
|
||
- **A full disk while saving a file was not reported.** The program saving it now gets the error
|
||
when it closes the file. Copying a large file into the drive no longer stalls everything else at
|
||
the end, `fsync` on a file opened for reading no longer fails, and `fallocate` modes the drive
|
||
cannot honour are refused instead of corrupting the file (B222, B224, B227).
|
||
- **A damaged settings file was replaced with defaults at the next settings change**, losing the
|
||
mount location and the ignore rules. Settings are no longer saved over a file that cannot be
|
||
read (B223).
|
||
- **Google Photos import gave some photos another photo's name and date.** A photo without its
|
||
own metadata file took a similarly named one's, and an edited copy took its original's name. An
|
||
edited copy now takes its original's date and keeps its own name (B225).
|
||
- **Thumbnails of portrait photos lay on their side**, here and in Proton's own apps (B226).
|
||
- **A write could be lost to a power cut after the program saving it had closed the file.** Saving
|
||
a change into the upload queue is now written through to disk (B229).
|
||
- **A partly written file whose upload answer was lost came back as a conflict copy of itself.**
|
||
The retry now recognises its own upload (B228).
|
||
- **A file or folder created offline could merge with another device's of the same name** made
|
||
while this computer was offline (B230).
|
||
- **A large upload under a low upload limit never finished.** It ran out of time on every try,
|
||
took the drive offline and started over. Its time limit now allows for its share of the limit
|
||
(B231).
|
||
- **Every reconnect sent all failed uploads again at once,** including those Proton had refused
|
||
for a full quota or missing access, and failed uploads were retried no later than every two
|
||
minutes. Refused uploads now keep their wait, which grows to five minutes, and retries are
|
||
spread out (B237).
|
||
- **Signing out left the session valid on Proton.** It is now ended there too; `pdfs logout` says
|
||
so when Proton cannot be reached (B233).
|
||
- **Any folder could be synced, the home folder included,** which uploaded the app's own cache
|
||
and walked into the drive itself. The root of the file system, the home folder and the folders
|
||
above it, the Proton Drive folder, the app's own data and folders inside or around another
|
||
synced folder are now refused, also when restoring another computer's folders (B234).
|
||
- **Names with `&` or `<` showed as empty rows in the app**, and some names were drawn as
|
||
formatting, for example shared items, computer names and link addresses (B232).
|
||
- **Photos imported from Google Photos, re-dated, or uploaded from the app had no thumbnail** in
|
||
Proton's apps (B236).
|
||
- **Photos with no other date were filed under the day they were uploaded.** Their own EXIF date
|
||
is now used, for Google Photos imports and for uploads from the app (B244).
|
||
- **Scanning a Google Photos export with a large folder took up to half a minute.** 10,000 photos
|
||
now take a tenth of a second (B235).
|
||
- **Every photo opened in the viewer stayed in memory**, with its full-size image, until the app
|
||
quit. The Share dialog leaked the same way (B241).
|
||
- **Opening a large photo froze the app** while it was decoded (B242).
|
||
- **The app froze when opening a file while the drive did not respond.** It now falls back to
|
||
downloading the file after two seconds. Applications the app started also no longer linger as
|
||
zombie processes (B243).
|
||
- **`pdfs import-google-photos --wait` and `pdfs redate-photos --wait` succeeded when the run
|
||
failed.** They now exit with an error when the run failed, was cancelled or left photos failed,
|
||
and the app shows why an import failed (B239).
|
||
- **`pdfs mount`, `login`, `unlock` and `logout` could be core-dumped with the account's keys in
|
||
memory.** Only the background service was protected. A file named like an option, such as
|
||
`--json`, also broke the file manager's "Keep offline" script (B240).
|
||
- **Signing in while the system keyring was locked froze the app window** until the keyring was
|
||
unlocked (B246).
|
||
- **A program the background service started could keep it from being restarted when it hung**
|
||
(B245).
|
||
- **The tray's transfer line was not translated** (B247).
|
||
|
||
### Changed
|
||
- **Draining a large batch of offline changes no longer slows down with its size.** The queue is
|
||
indexed by name (schema 43, B238).
|
||
- **Release binaries are stripped of their symbols**: `pdfs` goes from 43 to 35 MB, `pdfs-app`
|
||
from 24 to 19 MB and `pdfs-tray` from 12 to 8 MB.
|
||
|
||
## [3.4.1] — 2026-10-16
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Fixed
|
||
- **After a password reset, the drive never mounted and the app stayed on "Connecting…".** A
|
||
reset gives the account new keys, and Proton locks the Drive volume that was encrypted to the
|
||
old ones. The service then failed every start with `failed to decrypt: missing key`. It now
|
||
starts a new My Files volume, as the web app does. The files in the locked volume can still be
|
||
restored from [drive.proton.me](https://drive.proton.me) with the previous password
|
||
(B210, [#40](https://github.com/narrrl/proton-drive-linux/issues/40)).
|
||
- **A blocked sign-in said the app version was no longer supported.** Proton refuses sign-in for
|
||
a while after it suspects abuse, and its message then asks you to update. The app and
|
||
`pdfs login` now say that Proton is refusing sign-in for now and that waiting helps
|
||
(B209, [#39](https://github.com/narrrl/proton-drive-linux/issues/39)).
|
||
- **`pdfs diagnose` failed the `database` check on a fresh install.** The daemon creates the
|
||
database after the first sign-in, so a missing one is now a warning.
|
||
|
||
## [3.4.0] — 2026-10-12
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Added
|
||
- **A binary cache for the Nix flake.** Every release and every build of `main` is uploaded to
|
||
`proton-drive-linux.cachix.org`, so NixOS installs download the client instead of compiling
|
||
it. See [Install → Binary cache](INSTALL.md#binary-cache).
|
||
([#37](https://github.com/narrrl/proton-drive-linux/issues/37))
|
||
- **Folders can be made available offline in the app.** The context menu, the details pane and
|
||
the selection bar now offer it for folders too. Every file in the folder and its subfolders
|
||
downloads in the background with its progress in the tray and the app, and pinned folders carry
|
||
the offline badge. A file kept through its folder shows as available offline but cannot be
|
||
made online only on its own. `pdfs pin` on a folder now returns at once instead of timing out
|
||
on a large folder, and pinned files download to disk instead of into memory.
|
||
|
||
### Fixed
|
||
- **Removing the package left the drive mounted, and deletes there still reached Proton Drive.**
|
||
The service kept running from the removed program, so deleting `~/ProtonDrive` afterwards moved
|
||
its files to the Proton Drive trash. The .deb, .rpm and Arch packages now stop the service of
|
||
every logged-in user before they are removed. Upgrades are not affected.
|
||
- **`pdfs move` between two locations could move a folder with a file open for writing in it**,
|
||
when the file had been opened just after it first reached Proton Drive. The write then landed
|
||
after the move. Such a move is refused now, as it always was meant to be (B207).
|
||
- **The Fedora package installed KDE packages on GNOME.** Its recommendation of KWallet is now
|
||
only for Plasma, the GNOME keyring only for desktops other than Plasma, and the AppIndicator
|
||
extension only for GNOME.
|
||
|
||
## [3.3.0] — 2026-10-11
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Added
|
||
- **A Nix flake for NixOS.** `nix build github:narrrl/proton-drive-linux` builds the client, and
|
||
the flake's NixOS module installs it and runs the mount in every graphical session. Pin the
|
||
input to a release tag such as `github:narrrl/proton-drive-linux/v3.3.0` to stay on releases.
|
||
See [Install → NixOS](INSTALL.md#nixos-flake).
|
||
([#37](https://github.com/narrrl/proton-drive-linux/issues/37))
|
||
|
||
### Fixed
|
||
- **A failed photo upload said "You're offline" while everything else worked.** Only a failed
|
||
connection now reads as offline. Other failures show what went wrong, and Upload is greyed out
|
||
on an account without a photo library.
|
||
([#38](https://github.com/narrrl/proton-drive-linux/issues/38))
|
||
- **After an upgrade, the old version kept running in the background, and nothing said so.**
|
||
Installing a package does not restart the service, so fixed bugs, such as an unreadable folder
|
||
stopping a synced folder, came back until the next login. The tray, the app, `pdfs status` and
|
||
`pdfs diagnose` now say when the service is older than the app, and the tray and the app offer
|
||
to restart it. ([#33](https://github.com/narrrl/proton-drive-linux/issues/33))
|
||
|
||
## [3.2.1] — 2026-10-11
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Fixed
|
||
- **Writing to a file again soon after a partial write could fail with an I/O error.** When the
|
||
first write changed only part of a file this client had not read, a second write, or saving
|
||
over the file, failed until the first one reached Drive. It now fetches the rest of the file
|
||
and goes ahead.
|
||
- **A CAPTCHA could not be solved on hardened systems.** When the verification page cannot open in
|
||
the app, as on secureblue, the app now offers it in your browser and continues the sign-in
|
||
once you are done. `pdfs login` now handles a CAPTCHA the same way instead of failing.
|
||
([#29](https://github.com/narrrl/proton-drive-linux/issues/29))
|
||
- **The daemon kept several CPU cores busy while offline with changes queued.** Its upload
|
||
workers checked the queue in a tight loop for as long as the outage lasted. They now sleep
|
||
until the connection comes back. ([#35](https://github.com/narrrl/proton-drive-linux/issues/35))
|
||
- **An expired sign-in was shown as "offline" for good.** When Proton refuses the session, the
|
||
tray now says "Session expired — sign in again" with a Sign In Again… item, the app shows the
|
||
same with a button, and `pdfs status` and `pdfs diagnose` tell you to run `pdfs login`. Cached
|
||
files stay readable and changes stay queued until you sign in.
|
||
([#35](https://github.com/narrrl/proton-drive-linux/issues/35))
|
||
- **The sign-in could be lost after `pdfs login`, `pdfs unlock` or a slow network.** The old
|
||
daemon wrote its spent tokens over a new sign-in when it stopped, a timeout could drop a token
|
||
refresh halfway, and a refresh was retried with a token Proton had already used. Each process
|
||
now writes only into its own session, a refresh always finishes, and it is never repeated.
|
||
([#35](https://github.com/narrrl/proton-drive-linux/issues/35))
|
||
- **A sign-in refreshed while the keyring was locked was lost on the next restart.** The client
|
||
now keeps retrying, holds the new tokens in the kernel keyring meanwhile, and the tray says
|
||
"Sign-in not saved — unlock your keyring" until it is written.
|
||
|
||
## [3.2.0] — 2026-10-08
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Fixed
|
||
- **One unreadable folder stopped a synced folder from syncing at all.** A folder you may not
|
||
read, such as a cache a container created, failed every pass, so nothing else in the synced
|
||
folder reached Drive. Such a folder is now left out and named once in the activity feed, and
|
||
everything around it syncs. A file deleted during a scan, and a name that is not UTF-8, no
|
||
longer fail the pass either.
|
||
([#33](https://github.com/narrrl/proton-drive-linux/issues/33))
|
||
- **Switching a folder to online-only could delete files that were never on Drive.** Ignored
|
||
folders such as `.git/`, symlinks, unreadable folders and files changed after the last sync were
|
||
deleted with the local copy. The switch now refuses and names what is only on this computer.
|
||
- **Switching to online-only trashed synced files that had become ignored.** Adding a synced path
|
||
to `.pdfsignore` and then switching moved it to the Drive trash. Ignoring never deletes now, as
|
||
the user guide says.
|
||
- **A stopped share stayed on the Shared by me page for minutes.** Stop Sharing now takes it off
|
||
the page at once, and an emptied share no longer shows with no people and no link.
|
||
- **The tray icon showed syncing every two minutes.** The routine check of a synced folder no
|
||
longer counts as syncing. Only work that changes files spins the icon. An edit in one synced
|
||
folder also no longer rescans all the others.
|
||
- **Right-click menus sometimes opened too short and had to be scrolled.**
|
||
- **The Share dialog looked unfinished.** Its buttons now sit under their sections instead of in a
|
||
row of their own. The role is chosen beside the addresses, people show with avatars, and both
|
||
sections show that they are loading.
|
||
- **Breadcrumbs spaced the "›" unevenly, and grid file names were broken with hyphens they do not
|
||
have.**
|
||
|
||
### Changed
|
||
- **One main menu.** The menu button beside "Proton Drive" is gone. Preferences, Keyboard
|
||
Shortcuts, About and Sign Out are all in the account row at the bottom of the sidebar. F10 opens
|
||
it.
|
||
|
||
### Added
|
||
- **Move another computer's backup to this one.** **Move to This Computer…** on the Computers page,
|
||
or `pdfs devices migrate UID`, moves every folder another computer backs up, for example an old
|
||
Windows PC, into this computer's backup on Proton Drive. Then it offers them for syncing here. The
|
||
folders move on Proton Drive itself, so nothing is uploaded again. The other computer stays in the
|
||
account, without those folders.
|
||
|
||
## [3.1.2] — 2026-10-08
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Fixed
|
||
- **The app crashed when Proton asked for a CAPTCHA on hardened systems.** On secureblue, WebKit
|
||
cannot start the sandbox for the verification page and ends its process, which took the app
|
||
down with it. The page now opens in a separate window process: if that process fails, the
|
||
sign-in stops with a message that says why, and TROUBLESHOOTING.md has a workaround.
|
||
([#29](https://github.com/narrrl/proton-drive-linux/issues/29))
|
||
- **On a fresh install the tray icon did not start.** The tray and the app now create their state
|
||
directory themselves instead of waiting for the service to do it. This also lets the Takeout
|
||
page remember staged archives before the first mount.
|
||
([#29](https://github.com/narrrl/proton-drive-linux/issues/29))
|
||
|
||
## [3.1.1] — 2026-10-07
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Changed
|
||
- **The client identifies itself with its release.** Sign-in and every API call used to send the
|
||
fixed `external-drive-linux@0.1.0-alpha` and `proton-drive-linux/0.1.0`. If Proton retired that
|
||
version, every release would fail to sign in at once and no update could fix it. The client now
|
||
sends `external-drive-linux@<version>-stable` and `proton-drive-linux/<version>`. `app_version`
|
||
and `user_agent` in `config.json` are optional overrides now. The fixed values that releases up
|
||
to 3.1.0 wrote there count as unset and are removed on the next save. A value you set by hand is
|
||
kept.
|
||
- The About dialog shows the release version, and its debug info shows the identity that is
|
||
actually sent.
|
||
|
||
## [3.1.0] — 2026-10-07
|
||
|
||
Schema: **42**, unchanged.
|
||
|
||
### Added
|
||
- **Two-password accounts.** An account with a separate mailbox password now signs in: the app
|
||
and `pdfs login` ask for it after the login password and check it against the account, so a
|
||
typo is a retry and not a daemon that never mounts. The mailbox password is never stored.
|
||
What it unlocks is kept in kernel memory for the login session, so it is asked for once per
|
||
session and is gone after a reboot or when you log out of the machine.
|
||
- **`pdfs unlock` and `pdfs lock`.** `pdfs unlock` enters the mailbox password of a locked
|
||
account and starts the daemon's mount; `pdfs lock` forgets it again. `--remember` on `login`
|
||
and `unlock` (and the "Keep unlocked on this computer" switch in the app) keeps the unlock in
|
||
the system keyring instead, so the daemon starts without asking; `pdfs lock --forget` removes
|
||
it. Off by default: it gives up the separation a second password is for.
|
||
- **Locked state.** `pdfs status`, `pdfs diagnose` and the tray say when the account is waiting
|
||
for its mailbox password, and the app asks for it when it opens.
|
||
|
||
### Changed
|
||
- **The keyring no longer holds a password.** The session blob keeps the keys' passphrases
|
||
instead of the mailbox password, and a blob written by 3.0.x is converted on the first start.
|
||
A stored password the account rejects (a two-password account that was signed in with only
|
||
its login password) leaves the account locked instead of failing every start.
|
||
- **The daemon is not dumpable.** It holds the unlock in memory, so core dumps and same-user
|
||
`ptrace` are off for it.
|
||
- **The offline cache is re-keyed** from the new secret, so the first start after the update
|
||
reads it as cold and fetches the tree again once.
|
||
|
||
### Fixed
|
||
- **systemd killed a daemon that was only retrying.** `WatchdogSec` was answered only once a
|
||
mount existed, so a daemon waiting for a login or retrying a failed mount was aborted after
|
||
two minutes and restarted in a loop, hiding the error it logged. It now answers the watchdog
|
||
while it waits. ([#26](https://github.com/narrrl/proton-drive-linux/issues/26))
|
||
- **A refreshed token could overwrite a newer unlock or revive a signed-out session.** Token
|
||
rotation now updates only the tokens in the stored blob.
|
||
|
||
## [3.0.0] — 2026-10-05
|
||
|
||
Schema: **42**. The queue table is rebuilt so an id is never given out twice; queued changes
|
||
are kept. Queued changes get a column for the reason Drive refused them, and two for the
|
||
content a new file was sent with and where, and name their file and folder by local id as well
|
||
as by Drive's id. Stored files and folders name their parent
|
||
folder by local id too. A file or folder made on this machine that Drive does not have yet goes
|
||
by its local id, in the queue, the stored tree and pins alike.
|
||
|
||
### Added
|
||
- **Sync issues.** A change Drive refuses because the storage is full, access was taken away,
|
||
its folder is gone, a folder limit is reached or Drive rejected it shows up at once as needing
|
||
attention, with what it means and what to do, instead of after six failed attempts. The
|
||
change stays queued and is still retried.
|
||
- **Export a queued file.** The Sync page's Export button and `pdfs sync export <id> <dest>`
|
||
save a copy of a queued upload's content. The upload stays queued.
|
||
- **`pdfs sync issues`** lists the changes that need attention; `pdfs sync queue` shows the
|
||
issue next to each change.
|
||
- **Discard a queued change.** The Sync page's Discard button and `pdfs sync discard <id>` drop
|
||
a change Drive will not take and undo it here: a new file or folder goes, anything else goes
|
||
back to what Drive has. Export a file first to keep its content.
|
||
- **Sync issues show on the file.** A file or folder with a change Drive refused carries a
|
||
warning badge in the file browser, and its tooltip says what it means and what to do.
|
||
|
||
### Changed
|
||
- **Making, renaming and deleting through the mount no longer waits for Drive.** A new folder,
|
||
a new file, a rename or a delete is recorded locally and the call returns at once; the queue
|
||
sends it, as it does offline. `"local_first": false` in `config.json` brings back the old way
|
||
for this release.
|
||
- **The queue sends up to 16 changes at once instead of 3.** A thousand new files in a hundred
|
||
folders reach Drive about three times as fast on a LAN, and four times as fast on Wi-Fi.
|
||
|
||
### Fixed
|
||
- **A file made under the name of a file whose upload is still going out keeps its name.** It
|
||
no longer lands as a conflict copy, and the mount no longer shows that copy's name (B154).
|
||
- **A change to a file no longer lands as a conflict copy of itself when the link loses Drive's
|
||
answer to its upload** (B155).
|
||
- **A file written again while its upload was being retried no longer lands as a conflict copy
|
||
of itself when the link lost Drive's answer to the first try** (B156).
|
||
- **Writing to a file twice in a row no longer fails with "Input/output error" when the link
|
||
lost Drive's answer to reading back the file's last upload** (B157).
|
||
- **A file renamed twice in a row, while the first name was still taken, keeps the second name**
|
||
(B158).
|
||
- **Opening a file right after the daemon restarts no longer fails with "Resource temporarily
|
||
unavailable"** (B159).
|
||
- **A new folder, or a new empty file, reaches Drive at once**, not up to half a minute later
|
||
(B160, B192).
|
||
- **A file moved while it was still being uploaded keeps its new name in the mount** on a slow
|
||
link (B161).
|
||
- **A folder removed and made again at once keeps what goes into it.** It used to be created
|
||
inside the removed folder and was trashed along with it (B162).
|
||
- **A new file no longer reads as empty just after it is uploaded.** Drive can list it a moment
|
||
before its contents are committed, and the mount took that answer (B163).
|
||
- **A folder no longer lists a new file twice while it is uploaded.** A listing taken as the
|
||
upload landed showed it both as Drive had it and as the queue did (B164).
|
||
- **Stopping the daemon no longer hangs until systemd kills it.** A stop waits up to 10 s for
|
||
work in progress, then exits and logs what was still running (B165).
|
||
- **A folder no longer drops or empties a new file while others are uploaded.** A file whose
|
||
upload landed while the folder was being listed was missing from that listing, or listed as
|
||
empty (B166).
|
||
- **The mount comes up faster on a large Drive.** Rebuilding the folder listings from the cache
|
||
took close to 2 s with 37,000 files at every start (B167).
|
||
- **Files deleted with their folder while uploading stay deleted.** The mount no longer tries
|
||
to upload them to the top of your Drive instead (B168).
|
||
- **Stopping the daemon during a folder sync no longer waits for the sync.** The uploads in
|
||
progress finish, and the rest waits for the next start (B169).
|
||
- **Writing to a file again just as its upload finishes no longer fails.** A write right after
|
||
it failed with "Input/output error" (B170).
|
||
- **A file deleted while its upload was retrying is removed from Drive right away.** It no
|
||
longer waits for the upload's next retry (B171).
|
||
- **A file deleted just as it reached Drive is removed from Drive too.** It no longer stayed
|
||
there, and the next file with that name no longer became a conflict copy (B173).
|
||
- **Renaming a file just as it reaches Drive no longer fails with "Permission denied"**
|
||
(B174).
|
||
- **A file replaced by a rename just as it reached Drive is removed from Drive too.** It no
|
||
longer stayed there, and the next rename of its name no longer moved it instead (B175).
|
||
- **A write closed just as its file reached Drive is no longer lost** (B176).
|
||
- **Opening a folder just as it reaches Drive no longer fails with "Input/output error"**
|
||
(B177).
|
||
- **Deleting a folder while sync is paused no longer leaves a queued change behind** for a
|
||
file in it that was never uploaded (B178).
|
||
- **A file read just as it reaches Drive no longer reads empty** (B179).
|
||
- **Stopping the daemon no longer takes 10 seconds more than it needs** when an idle upload
|
||
worker missed the stop (B180).
|
||
- **A folder made while offline or with sync paused no longer fails with "Input/output
|
||
error"** once it is refreshed or the connection comes back (B125).
|
||
- **The daemon no longer hangs when a file being read changes on Drive** (B130).
|
||
- **The Activity page says in your language why an edit was kept as a conflicting copy**,
|
||
instead of the daemon's English (B181).
|
||
- **A file written after a restart no longer lands as a conflict copy of itself** when the
|
||
connection dropped just as it first reached Drive (B182).
|
||
- **A file deleted after a restart is removed from Drive too** when the connection lost Drive's
|
||
answer to its upload and the file was written again meanwhile. It no longer stayed there and
|
||
turned the next file under its name into a conflict copy (B183).
|
||
- **A file renamed while the connection lost Drive's answer to its upload lands once.** It no
|
||
longer stayed on Drive under its old name too, or, when deleted, under its old name alone
|
||
(B184).
|
||
- **A new file written again just as it starts uploading no longer waits out a retry** before
|
||
it reaches Drive (B185).
|
||
- **A file deleted while open can still be read and written until it is closed**, offline too.
|
||
A write into it no longer failed with "Input/output error" (B186).
|
||
- **An uploaded file no longer leaves a copy of itself in the cache** until the next start
|
||
(B187).
|
||
- **Changes that failed during a short outage go out soon after the connection is back**, not
|
||
after a retry delay of up to five minutes (B189).
|
||
- **A file saved just before the daemon stops reaches Drive** with what was written. It could
|
||
keep its old content, or stay empty (B190).
|
||
- **A file renamed just as its upload finished keeps its new name.** The mount no longer showed
|
||
the old name, and a second rename no longer failed with "No such file or directory" (B152,
|
||
B172).
|
||
- **A change that waits for a name to be freed no longer waits seconds after it is.** A rename or
|
||
upload over a file being deleted goes out as soon as the delete lands (B153).
|
||
- **A file deleted right after it was made no longer stays on Drive when the link lost Drive's
|
||
answer.** The node Drive made is trashed (B151).
|
||
- **A new file no longer lands twice when the link loses Drive's answer to its upload.** The
|
||
file Drive made is taken as it is, instead of a second upload as a conflict copy (B127).
|
||
- **A change to a file deleted elsewhere, together with its folder, no longer retries forever.**
|
||
The conflict copy that keeps it now lands in the location's root (B150).
|
||
- **`pdfs rename` and `pdfs move` right after a change through the mount are no longer undone.**
|
||
They are queued behind it, and they work on a file that has not reached Drive yet (B149).
|
||
- **A folder kept offline before it reached Drive stays kept offline.** Its pin was lost when the
|
||
folder landed (B148).
|
||
- **A queued change no longer holds up the queue forever on a link that stops answering.**
|
||
Sending a new folder, a delete or a file waits at most 20 seconds for Drive, plus time for
|
||
the file's size, and then the queue treats the link as down and tries again.
|
||
- **A file deleted while its first upload is on the wire no longer stays on Drive.** The node
|
||
the upload made is trashed as soon as it lands, so its name is free again (B129).
|
||
- **A delete queued while a rename is sent is no longer lost.** The rename landing could remove
|
||
the delete from the queue, leaving the file on Drive (B137).
|
||
- **A file made offline over one deleted offline is no longer lost.** Its upload could take
|
||
over the deleted file, and the delete then took the new bytes along with it (B128).
|
||
- **A file deleted while its upload is on the wire no longer comes back as a `recovered-…`
|
||
copy** when another file is made right after (B133).
|
||
- **A file made where another one is leaving no longer lands as a conflict copy.** Its upload
|
||
waits for the rename or trash that frees the name (B126, B138).
|
||
- **A file closed just as its first upload lands keeps its last write.** The write was refused
|
||
and left in staging (B139).
|
||
- **Files made in a new folder are still there after a restart.** A folder that landed as the
|
||
daemon stopped, or one listed from Drive while files in it were queued, came back empty
|
||
(B132, B140).
|
||
- **A file renamed twice in quick succession ends up under the second name.** The first rename
|
||
could land after the second and undo it (B141).
|
||
- **Renaming a new file just as its upload lands no longer fails with "Device or resource
|
||
busy".** The rename goes to the file Drive made (B142).
|
||
- **A file whose upload landed just as the connection dropped no longer gets a conflict copy at
|
||
its next save.** The daemon reads the file back once the connection returns (B143).
|
||
- **A rename whose target name was just freed no longer lands as a conflict copy.** The rename
|
||
is sent once more under the name it asked for (B144).
|
||
- **Saving a download under its final name no longer fails with "Device or resource busy".**
|
||
A file made a moment before was taken for deleted and dropped from the queue (B145).
|
||
- **A file renamed just as its upload lands keeps its new name.** The tree could take back the
|
||
name Drive still had (B146).
|
||
- **A new file made under a name another file just left no longer takes that file's place on
|
||
Drive.** Both could land on one node, and one file's content was lost (B147).
|
||
|
||
## [2.10.0] — 2026-10-03
|
||
|
||
Schema: **36**. Every node row gets a local id that never changes; existing rows keep their
|
||
row number as that id. Going back to 2.9 means rebuilding the cache. Requires `proton-drive-rs`
|
||
**0.7.2**.
|
||
|
||
### Added
|
||
- **Migration tests on real databases.** A database 2.8.2 wrote, with queued changes due,
|
||
parked, failing, waiting for access and in flight, migrates with every change intact.
|
||
`PDFS_MIGRATE_DB=<path> cargo test -p pdfs-core a_copy_of_a_real_database -- --ignored` runs
|
||
the same check on a read-only copy of any database.
|
||
|
||
### Changed
|
||
- **A file keeps its inode number across daemon restarts.** Inodes are now the node's local id
|
||
instead of a counter that started over on every run.
|
||
- **Every mount serves from one tree.** An on-demand sync folder used to keep its own copy of the
|
||
nodes it showed, which background work had to visit one by one and could miss (B74). It now
|
||
shares the tree with My files; only the root it starts at is its own.
|
||
|
||
### Fixed
|
||
- **A file renamed or moved while its first upload is on the wire keeps its new name and folder.**
|
||
It landed under the old name and the rename was lost (B136).
|
||
|
||
## [2.9.0] — 2026-10-03
|
||
|
||
Schema: **35** (unchanged). Requires `proton-drive-rs` **0.7.2**.
|
||
|
||
### Added
|
||
- **Seeded simulation runs.** `cargo test -p pdfs-fuse --lib sim:: -- --ignored
|
||
--test-threads=1` mounts one or more real FUSE daemons against an in-memory Drive that lags,
|
||
drops, reorders and echoes like the real one. Each seed draws syscalls, link flaps and restarts,
|
||
checks every answer against a POSIX model, and at the end checks that nothing was lost and the
|
||
clients converged. CI runs them on pushes to main. They found B122 to B135.
|
||
- **More acceptance cases.** Regressions for B48, B80, B86, B88, B94, B95, B98, B102, B111, B113
|
||
and B114 to B121, and a check that `pdfs` output into a closed pipe stays quiet. The console
|
||
report has one line per case and a summary table, and `--journal-check` also fails on a
|
||
conflict copy, a FUSE job held for long, or an authority re-read.
|
||
|
||
### Fixed
|
||
- **A new file moved or renamed before its first upload no longer lands as a conflict copy**
|
||
next to its own empty original (B113).
|
||
- **`pdfs rm`, `pdfs rename` and `pdfs mkdir` take paths in on-demand folders.** They answered
|
||
"is not under the mountpoint" for a path `pdfs move` took. `rm` and `rename` refuse a path that
|
||
names a mount's root (B88).
|
||
- **`pdfs ls | head` no longer panics** with "Broken pipe". A closed stdout exits 0; any other
|
||
write error exits 1.
|
||
- **A file removed, renamed or moved with `pdfs` stops resolving under its old name at once**,
|
||
instead of for up to 30 seconds (B114).
|
||
- **`pdfs ls` and the app's file browser show a file's real size**, not its encrypted one
|
||
(B115).
|
||
- **Trashing a folder also drops the creates queued in its subfolders.** One could stay queued
|
||
with its bytes, or land in the root (B116).
|
||
- **An upload queued for a file `pdfs move` moved keeps its path** in `pdfs sync queue` and is
|
||
no longer put off once after the resume (B117).
|
||
- **Creating a file or folder through the mount no longer fails with `ENOENT`** when Drive is
|
||
slow to list what it made. The mount asks again, and queues the create if it still cannot see
|
||
it (B118).
|
||
- **A file just written can be read without the network.** Its uploaded bytes stay cached, and
|
||
`pdfs status` counts the upload as pending until they are (B119).
|
||
- **A file queued inside a folder that just landed is no longer put off** for five seconds
|
||
(B120).
|
||
- **A move right after a rename lands.** It failed with `Input/output error` through the mount
|
||
and with "out of date" from `pdfs move`, and a rename across folders took up to 10 seconds
|
||
(B111, B121).
|
||
- **A rename across folders no longer fails with `Input/output error`** when the source folder
|
||
holds the new name (B122).
|
||
- **A second write open on a file just written no longer fails** with `Input/output error`
|
||
(B123).
|
||
- **After a replacing rename fails, the file it was replacing stays in the mount** (B124).
|
||
- **A write to a file whose create could not be read back no longer lands as a conflict copy**
|
||
(B131).
|
||
- **A file just moved is found by its new path.** A lookup right after the rename could miss it
|
||
(B135).
|
||
|
||
### Notes
|
||
- The simulation runs found bugs that are still open: B125 to B130 and B132 to B134 in
|
||
`docs/BUGS.md`. The worst are B127 and B128 (a file created offline can land twice, or be lost
|
||
when created over one deleted offline), B129 (a file deleted while its create is on the wire
|
||
stays on Drive) and B130 (a read that a remote change invalidates can hang the daemon).
|
||
|
||
## [2.8.2] — 2026-10-01
|
||
|
||
Schema: **35** (unchanged). Requires `proton-drive-rs` **0.7.2**.
|
||
|
||
### Fixed
|
||
- **Folders shared with you no longer go missing when one person shared several.** When
|
||
several accepted shares came from the same owner's volume, only the first appeared under
|
||
Shared with me, and the rest were skipped with `missing key`. `proton-drive-rs` 0.7.2 resolves
|
||
each shared root with its own key (B112, #25).
|
||
|
||
## [2.8.1] — 2026-09-30
|
||
|
||
Schema: **35** (unchanged). Requires `proton-drive-rs` **0.7.1**.
|
||
|
||
### Fixed
|
||
- **The service no longer starts before the desktop session.** `proton-drive.service` was
|
||
wanted by `default.target`, which is reached before the desktop hands `DISPLAY` /
|
||
`WAYLAND_DISPLAY` to the user's systemd. The first keyring call then started the Secret
|
||
Service without a display; on KDE `ksecretd` crashed at every login and the first start of the
|
||
service failed. The unit is now wanted by `graphical-session.target`. Existing installs switch
|
||
over on the next sign-in or "Start on login" toggle, or with
|
||
`systemctl --user reenable proton-drive.service` (#23).
|
||
|
||
## [2.8.0] — 2026-09-30
|
||
|
||
Schema: **35** (unchanged). Requires `proton-drive-rs` **0.7.1**.
|
||
|
||
### Added
|
||
- **`pdfs trash --json`** prints the trash listing as JSON.
|
||
- **The FUSE acceptance suite tests the signed-in account by default.**
|
||
`scripts/fuse-acceptance.sh` with no arguments creates test folders in My files and new
|
||
on-demand and mirror sync folders, runs every case in each, and removes all of it afterwards,
|
||
also on a failure or interrupt. A run that dies is cleaned up by the next one. New cases cover
|
||
block boundaries, unusual names, a deep tree, a wide directory, rename patterns, handle coherency,
|
||
throughput and B100. `--offline-only` runs the account-free suite as before.
|
||
|
||
### Changed
|
||
- **The mount keeps working through a network drop.** When Drive stops answering, the daemon
|
||
now marks itself offline within about 20 seconds instead of failing every call for up to two
|
||
minutes. While offline, creating, renaming and deleting files and folders is queued instead of
|
||
failing with `Input/output error`, folders list from the local cache, and a read that needs
|
||
data from Drive waits up to 30 seconds for the connection. The daemon checks every few seconds
|
||
for the connection to return, then uploads the queued changes. A file whose creation timed
|
||
out but reached Drive anyway is taken over rather than uploaded again as a conflict copy.
|
||
- **A dead connection is noticed sooner.** `proton-drive-rs` 0.7.1 sends TCP keepalives, so a
|
||
connection that died silently fails instead of waiting for its request timeout.
|
||
|
||
### Fixed
|
||
- **Truncating and then writing past the end no longer brings old bytes back.** The gap
|
||
between the new end and the write now reads as zeros, as it should, instead of the file's
|
||
earlier content. When that earlier content had not been uploaded yet, the file failed with
|
||
`Input/output error` and its upload retried forever (B101).
|
||
- **A write made while a new file is first uploading is no longer lost.** Drive could keep an
|
||
empty or older copy of a file written right after it was created, and closing a file held open
|
||
across its first upload could fail (B101).
|
||
- **Removing a folder also cancels the pending uploads of the files inside it.** Before, an
|
||
edit that had not reached Drive yet kept retrying against the trash forever and kept its
|
||
staged copy on disk. `pdfs rm` now also drops what was queued for what it removes (B102).
|
||
- **A program reading a file keeps its data when another program replaces the file.** Saving
|
||
over a file by renaming a new copy onto it (what most editors and `os.replace` do) made
|
||
reads through handles already open on the old file fail with `Input/output error` (B104).
|
||
- **Short-lived files no longer come back as conflict copies.** A file deleted right after it
|
||
was written, such as a SQLite journal, could be uploaded as a `recovered-…` conflict copy in
|
||
the Drive root (B105).
|
||
- **Editors can save over a file several times in a row.** The fix for B104 could leave the
|
||
upload of a replaced file queued, and the next save onto the same name failed with
|
||
`Input/output error` (B106).
|
||
- **Renaming a file back right after renaming it no longer fails.** Drive can briefly call the
|
||
second rename out of date, and the daemon now waits it out instead of returning an I/O error
|
||
(B111).
|
||
- **Reading a freshly listed file no longer hangs.** When a read had to fetch the file's real
|
||
size first, the daemon could wait on the read it was serving, and the program froze until
|
||
the daemon restarted (B110).
|
||
- **Databases and other files kept open while written no longer read back empty.** When the
|
||
folder was listed again while a file was still open, its size dropped to what Drive held,
|
||
often 0 bytes, and SQLite reported `database disk image is malformed` (B109).
|
||
- **Deleting files during a mirror pass no longer marks the folder as failed.** A file removed
|
||
after the pass planned its upload was counted as an error, so the folder showed `error` until
|
||
a later pass (B108).
|
||
- **A read no longer hangs when the connection stalls.** A read that gets no answer from Drive
|
||
within two minutes now fails with `Input/output error` instead of blocking the program for
|
||
as long as half an hour (B107).
|
||
- **The trash no longer freezes after a server call hangs.** Until the daemon restarted, the
|
||
trash kept showing the same entries, and a folder just deleted could not be found there to
|
||
delete permanently (B103).
|
||
- **Freshly listed files read correctly.** A file whose real size had not been fetched yet
|
||
reported its encrypted size, about 53 bytes too big, and reading it failed with
|
||
`Input/output error`. This happened most often right after copying a large folder. Reads and
|
||
write opens now fetch the real size first, and a big folder walk no longer skips size lookups
|
||
(B100).
|
||
|
||
## [2.7.0] — 2026-09-29
|
||
|
||
Schema: **35** (unchanged).
|
||
|
||
### Added
|
||
- **Moves between locations without transferring content.** `pdfs move` and the app's
|
||
**Move to…** now take a file or folder from My files into a synced folder, or between two
|
||
synced folders, on-demand or mirrored. The move happens on Proton Drive, so nothing is
|
||
downloaded or uploaded again. `mv` between two locations still copies, because the kernel
|
||
refuses a rename between two mounts. The app's Move dialog lists every location at its top
|
||
level. A move is refused, with the reason, when the source still has changes that are not on
|
||
Proton Drive, when the two locations are on different volumes, or when a synced folder stays
|
||
busy with a sync pass.
|
||
- **`pdfs move` takes several sources.** `pdfs move a b c DEST` moves all three and
|
||
reports each one that could not move.
|
||
- **Moving into a mirrored folder reuses cached content.** A file already cached by
|
||
an online-only folder is copied into place instead of downloaded again.
|
||
|
||
### Changed
|
||
- **New project homepage: [proton-drive.narl.io](https://proton-drive.narl.io).** The About
|
||
dialog's website link, the README, and the package metadata (Cargo, PKGBUILDs, `.deb`,
|
||
`.rpm`) point there. The source code link stays on GitHub.
|
||
|
||
## [2.6.2] — 2026-09-28
|
||
|
||
Schema: **35** (unchanged).
|
||
|
||
### Fixed
|
||
- **Photos remembers the Albums and Places views.** Leaving Photos from Albums or Places and
|
||
coming back showed the timeline while the Albums or Places toggle stayed selected. The page
|
||
now reopens on the selected view, and Places keeps its Grid or Map layout.
|
||
- **The Sync page keeps a steady width.** A long file name in Transfers no longer widens the
|
||
page. Overview and Folders now fill the width next to the sidebar.
|
||
|
||
## [2.6.1] — 2026-09-28
|
||
|
||
Schema: **35** (unchanged).
|
||
|
||
### Added
|
||
- **`pdfs-prompt --preload`.** Starts the launcher hidden, for session autostart such as
|
||
Hyprland's `exec-once`, so the first shortcut opens it as fast as later ones. It does nothing
|
||
when the launcher is already running.
|
||
|
||
### Fixed
|
||
- **Launcher invocations no longer pile up.** Each shortcut press after the first used to leave
|
||
a `pdfs-prompt` process behind after handing the request to the running launcher. It now exits.
|
||
|
||
## [2.6.0] — 2026-09-28
|
||
|
||
The search launcher (`pdfs-prompt`) is rebuilt around a preview pane, and it now looks like the
|
||
app it belongs to.
|
||
|
||
Schema: **35** (unchanged).
|
||
|
||
### Added
|
||
- **Launcher preview pane.** The selected result shows a large thumbnail, its type, size,
|
||
modified date, location and whether it is on this computer, with Open (Enter), Show in Folder
|
||
(Ctrl+Enter) and Copy Path (Ctrl+C, or Ctrl+Shift+C while text is selected in the search box).
|
||
Show in Folder selects the file in the file manager through `org.freedesktop.FileManager1`
|
||
and falls back to opening the folder.
|
||
- **Recent files.** With an empty search box, the launcher lists the last files opened from it
|
||
above the pinned ones. Files opened often and lately also rank higher in search results. The
|
||
history lives in `prompt-history.json` in the state directory and forgets local files that
|
||
no longer exist.
|
||
- **Thumbnails in the launcher.** Images show a thumbnail in the result list and the preview,
|
||
from the desktop's thumbnail cache, decoded locally (and written back to that cache), or
|
||
fetched by the daemon for Drive files.
|
||
- **Drag and drop from the launcher.** A result can be dragged into a file manager or another
|
||
application as a file.
|
||
|
||
### Changed
|
||
- **The launcher follows the app's theme.** It uses the system accent colour, light and dark
|
||
style, and the opt-in Proton theme, instead of always pinning Proton purple. Changes to the
|
||
theme or to `open_with` apply the next time the launcher opens.
|
||
- **Launcher layout.** Results are grouped into Recent, Pinned in Proton Drive and Best matches;
|
||
matched characters are bold, and each row shows its sync state, size and age. A click selects
|
||
a row and a double-click opens it; the search box keeps the keyboard focus throughout.
|
||
- **Faster search as you type.** The launcher searches 80 ms after the last keystroke instead
|
||
of 120 ms.
|
||
|
||
### Fixed
|
||
- **The launcher no longer freezes on a stuck mount.** Checking whether a Drive file is in the
|
||
mount runs off the main thread and gives up after 2 seconds, falling back to the daemon.
|
||
Escape cancels a Drive file that is still opening.
|
||
|
||
### Documentation
|
||
- **Rewritten user documentation.** New pages for installation, the user guide, the command-line
|
||
reference, configuration and troubleshooting, and a documentation index in `docs/README.md`.
|
||
The README is shorter and points to them.
|
||
- **Recovery** describes the current restore flow (adopting a computer, `pdfs sync restore`, the
|
||
machine profile) instead of the old manual runbook.
|
||
- **Architecture** covers push events, remote event streams, the revision block geometry, the
|
||
thread layout of 2.5.0 and the machine profile. The development page is now a developer guide,
|
||
and open work moved to `docs/ROADMAP.md`.
|
||
- `scripts/publish-wiki.sh` publishes the user pages to the GitHub wiki.
|
||
|
||
## [2.5.0] — 2026-09-28
|
||
|
||
A polish pass over the desktop app: the daemon pushes changes instead of being polled, pages
|
||
stay up while they reload, Trash and the shared pages use the same file list as My files, the
|
||
Sync page is split into views, the Computers page can browse another computer's backup, and
|
||
Photos gains a timeline scrubber.
|
||
|
||
Schema: **35**. The daemon notes when it first sees an item in the trash, since Proton does not
|
||
report the trash time to this client yet.
|
||
|
||
### Added
|
||
- **Browse other computers.** Opening another computer on the Computers page shows its backup
|
||
read-only, in the same list and grid as My files, and files open with a double-click.
|
||
- **Trash shows where items came from and when they were deleted.** The date is when this client
|
||
first saw the item in the trash until Proton reports the exact time. Restoring offers a Show
|
||
action, and opening a trashed item explains that it must be restored first.
|
||
- **Shared with me** shows who shared each item, with a warning for unverified senders, the
|
||
access it gives and when it was shared. Pending invitations wait in a banner, several items can
|
||
be left at once, and saved links have their own view.
|
||
- **Shared by me** gains a filter (links, people, expired links), columns for location, people
|
||
and link state, a copy button for live links, and stopping several shares at once.
|
||
- **Sidebar badges** count sync problems and waiting invitations.
|
||
|
||
### Changed
|
||
- **The app and the tray no longer poll.** The daemon pushes its status, its transfers and what
|
||
changed over one control connection (`Request::Subscribe`), and the window reloads the page on
|
||
screen when that page's data changes, including changes made with the `pdfs` CLI. A hidden
|
||
window keeps the changes and catches up when it is focused. Front-ends fall back to polling a
|
||
daemon that predates subscriptions until its service restarts.
|
||
- **Refresh buttons are gone** from the page headers now that pages follow the daemon. F5 and
|
||
Ctrl+R still reload the page from the server.
|
||
- **Photos timeline scrubber.** A strip along the right edge of the timeline shows the years
|
||
and appears when the pointer comes near or the timeline scrolls. Hovering names the month
|
||
under the pointer, and dragging jumps to it. It replaces the slider above the grid and the
|
||
scrollbar.
|
||
- **Upload is a split button** on the Photos page. Its menu holds "Import from Google Photos…",
|
||
and the page menu keeps "Find Duplicates…".
|
||
- **Quieter photo tiles.** Hovering enlarges the thumbnail slightly instead of lifting the tile
|
||
with a shadow, and tiles no longer show a tooltip. Tiles are reused while scrolling rather than
|
||
rebuilt for every row.
|
||
- **Newer libadwaita widgets where available.** On libadwaita 1.8 or newer the app uses its
|
||
spinner, toggle groups, button rows and shortcuts dialog, and on older releases it keeps
|
||
look-alike stand-ins. The choice is made at run time, so every package gets them and the app
|
||
still builds and runs on libadwaita 1.5. Set `PDFS_STAND_IN_WIDGETS=1` to see the stand-ins
|
||
on a newer system.
|
||
- **Sync page views.** Overview holds the status, conflicts, transfers and queue; Folders holds
|
||
the folder list, with a short explanation of the sync modes; History is the former Activity
|
||
page. A banner points back to Overview when changes fail or conflict. Each folder is one card
|
||
with a Synced / Online only switch, and switching either way asks first.
|
||
- **Activity reads better.** Temporary files that editors save through are hidden (a menu shows
|
||
them again), runs of similar events fold into one expandable row, entries are full translated
|
||
sentences, and "Show Older" reaches further back.
|
||
- **Computers** lists every computer in one group, this one first. "Use This Computer's
|
||
Identity" is now "Continue This Backup Here", with an explanation in its dialog, and "never
|
||
synced" reads "no backups yet".
|
||
- **Pages stay up while they reload.** A loading state shows only when a reply takes longer than
|
||
300 ms, and a refresh keeps the selection, scroll position and thumbnails.
|
||
- **One glossary.** "Proton Drive service" replaces "mount service" and "daemon", and "Online
|
||
only" replaces "On-demand"; dialogs use title case, and dates and sizes are formatted one way.
|
||
- **Bundled icons** replace glyphs that stock icon themes lack or draw ambiguously.
|
||
|
||
### Fixed
|
||
- **Enabling, disabling or restarting the service no longer freezes the window.**
|
||
- **A daemon that runs but doesn't answer** reads as "Not responding" instead of disconnected,
|
||
and requests to it give up after 5 s instead of 120 s.
|
||
- The Sync and share-link icons drew only half of themselves on recent GTK versions.
|
||
|
||
## [2.4.0] — 2026-09-28
|
||
|
||
Schema: **34**. Photos gain a `similar_hash` column for the similar-photo finder. It stays
|
||
empty until the finder is first opened, so updating reads and downloads nothing.
|
||
|
||
### Added
|
||
- **Similar photos.** The duplicate finder gains a Similar switch that also finds resized or
|
||
re-encoded copies and shots of one moment, by comparing the thumbnails. The first search
|
||
hashes every thumbnail the server has, with a progress bar; photos without one are not
|
||
downloaded and are counted as not compared. Every photo in a set starts ticked Keep, since
|
||
photos that look alike can be different shots, and a photo not kept goes to Trash with the
|
||
rest of its shot.
|
||
- **Photos on the map.** Every town is marked with one of its photos and a count, and nearby
|
||
towns merge into one marker until you zoom in. Zoomed in on a town, each photo gets its own
|
||
marker where it was taken; clicking one opens it in the viewer, and clicking photos taken
|
||
on one spot lists them all. "Show on map" in the photo viewer now goes to that spot on the
|
||
Places map instead of a web browser.
|
||
- **Street map for Places.** An opt-in setting under Appearance draws the map from
|
||
[OpenFreeMap](https://openfreemap.org/) (OpenStreetMap data), with streets, water, parks and
|
||
town names in your language. It is off by default because the tile server learns which parts
|
||
of the world you look at. Tiles are cached under the cache directory (at most 256 MB), and
|
||
the built-in outline shows wherever they haven't loaded.
|
||
- **Proton theme.** The "Proton purple accent" preference is now a whole Proton theme: the
|
||
Proton web apps' light and dark colours, not just the accent. It also holds when `gtk.css`
|
||
imports another GTK theme (Catppuccin, Fluent, ...), whose own colours used to show through
|
||
almost everywhere. Folders get Proton's purple folder icon rather than the icon theme's.
|
||
Anyone who had the accent turned on gets the theme.
|
||
- **Country borders on the Places map.** The offline map draws country borders, with sea and
|
||
land in their own colours.
|
||
|
||
## [2.3.0] — 2026-09-28
|
||
|
||
Schema: **33**. Photos gain their location; the first timeline refresh after updating reads
|
||
the metadata of the whole library once (nothing is downloaded), and the Places view shows how
|
||
far it has got.
|
||
|
||
### Added
|
||
- **Places.** A third Photos view groups photos by the town they were taken in, as cards or on a
|
||
world map. Towns are looked up offline in bundled GeoNames data (CC BY 4.0), and the map is
|
||
drawn offline from Natural Earth land outlines (public domain), so no map tiles are fetched.
|
||
Install `iso-codes`, a new optional dependency, for country names in your language.
|
||
- **On this day.** A strip at the top of the timeline shows a card for every earlier year with
|
||
photos taken on today's date.
|
||
- **Find duplicates.** Photos can list every set of byte-identical copies and move the copies
|
||
you don't keep to Trash. The copy in the most albums is kept by default.
|
||
- **Photos in no album.** A filter narrows the timeline to photos no album holds yet.
|
||
- **One Filters popover.** Favorites, "in no album" and the month jump now share a Filters
|
||
button, which shows how many filters are on and can clear them all at once.
|
||
- **Zoom-level grouping and range select.** Zooming out groups the timeline by month, then by
|
||
year. Shift+click selects a range, and dragging across the timeline selects what it sweeps.
|
||
- **Slideshow.** The photo viewer plays the loaded photos full screen, with F5 or a top-bar
|
||
button.
|
||
- **App icon in the app.** The sidebar header and the login page show the app's own icon.
|
||
|
||
### Fixed
|
||
- **Photos grid.** The timeline is laid out as square tiles that fill the window's width
|
||
exactly, and a theme's own `gtk.css` can no longer pad the tiles. It stays at its top, and at
|
||
the photo you were looking at, when the window is resized or zoomed.
|
||
- **Timeline shaking near the top.** Scrolling up to the "On this day" strip no longer makes the
|
||
whole page jump between two heights.
|
||
- **Filters while loading.** Changing a filter while a page was still loading could leave the
|
||
timeline showing the old filter.
|
||
|
||
## [2.2.2] — 2026-09-27
|
||
|
||
Schema: **32** (unchanged).
|
||
|
||
### Fixed
|
||
- **Conflicts in synced folders.** A conflict copy inside a synced folder is now listed under
|
||
Needs attention and can be resolved there or with `pdfs conflicts`. It used to be logged again
|
||
after every restart and marked "Resolved" while it was still on disk (B99).
|
||
|
||
## [2.2.1] — 2026-09-24
|
||
|
||
Schema: **32** (unchanged).
|
||
|
||
### Added
|
||
- **Legal information in About.** The About dialog now links to the source code and to Proton's
|
||
terms of service and privacy policy, and its Legal page carries the full disclaimer: this client
|
||
is unofficial, not affiliated with or endorsed by Proton AG, and comes without warranty.
|
||
|
||
## [2.2.0] — 2026-09-23
|
||
|
||
Schema: **32**. Videos that earlier got no thumbnail are tried again.
|
||
|
||
### Added
|
||
- **Video thumbnails.** Videos without a thumbnail from Drive now show a frame from the clip in
|
||
Photos. This needs `ffmpeg`, a new optional dependency.
|
||
- **Uploads carry a thumbnail.** Photos and videos uploaded from this client now include one, so
|
||
they show a preview in Proton's own apps as well, without downloading the whole file.
|
||
|
||
### Fixed
|
||
- **The Photos gallery no longer jumps** while thumbnails load, when zooming, or when switching
|
||
tabs and back.
|
||
- **The month scrubber no longer collides with the scrollbar.**
|
||
- **The photo details panel is no longer empty.**
|
||
- **Photo details read like a camera's:** "Google Pixel 6" instead of quoted EXIF values, shutter
|
||
speeds such as 1/19 s, and a labelled ISO.
|
||
|
||
## [2.1.1] — 2026-09-23
|
||
|
||
Schema: **31** (unchanged).
|
||
|
||
### Fixed
|
||
- **Uploads no longer get stuck at 0 bytes.** When a file changed again while its previous
|
||
version was uploading, such as a browser download that is still growing, the old upload could
|
||
hang forever. It kept one background thread busy at full CPU and showed as "Uploading" in the
|
||
Transfers list until the daemon restarted.
|
||
|
||
## [2.1.0] — 2026-09-23
|
||
|
||
The desktop app, the tray and the search prompt speak German, British English, Spanish, French,
|
||
Italian, Dutch, Polish and Brazilian Portuguese, picked from the system language or chosen in
|
||
Preferences.
|
||
|
||
Schema: **31** (unchanged).
|
||
|
||
### Added
|
||
- **Translations.** The desktop app, the tray and the search prompt are translated into German,
|
||
British English, Spanish, French, Italian, Dutch, Polish and Brazilian Portuguese, and follow the
|
||
system language. Counts use each language's plural rules, and dates use its own formats. The
|
||
desktop files carry translated descriptions and search keywords. The `pdfs` CLI and messages
|
||
from the daemon stay in English. [docs/TRANSLATING.md](TRANSLATING.md) describes how to improve a
|
||
translation or add a language. A few labels on the Photos page (the filter tabs, the gallery
|
||
subtitle and the heading shown when a filtered timeline is empty) are still in English.
|
||
- **Language preference.** Preferences → General → Appearance → Language overrides the system
|
||
language for the app, the tray and the prompt, from their next start.
|
||
|
||
### Changed
|
||
- **Packages install translations.** The Arch, AUR, Debian and Fedora packages install the compiled
|
||
catalogs under `/usr/share/locale`, and the release tarball carries them in `locale/`. Building
|
||
the source packages now needs `gettext`.
|
||
|
||
## [2.0.0] — 2026-09-23
|
||
|
||
A redesign of the desktop app: a new shell with a Preferences dialog, keyboard-driven context
|
||
menus, a docked details pane, a rewritten photo viewer with infinite scroll, album management,
|
||
stop sharing and link expiry, a redesigned Shared with Me page, per-device restore, a tray that
|
||
shows the sync state, and a new app icon.
|
||
|
||
Schema: **31** (unchanged).
|
||
|
||
### Added
|
||
- **Pause and resume sync.** The Sync page, the tray and `pdfs sync pause [--for 1h]` /
|
||
`pdfs sync resume` hold back queued uploads and mirror-folder reconciles. Reads through the mount
|
||
keep working, writes keep being accepted and staged, and a timed pause ends by itself. The pause
|
||
survives a restart.
|
||
- **See what is waiting to upload.** The Sync page lists the queue with each item's last error and
|
||
next retry, with Retry now per item and Retry All; `pdfs sync queue` and `pdfs sync retry [id]`
|
||
do the same from the CLI. Nothing in the queue is ever dropped from here.
|
||
- **Resolve sync conflicts.** `(sync-conflict …)` copies under My Files are listed on the Sync page
|
||
next to the file they conflict with, with sizes, times and whether the content differs. Keep the
|
||
original, keep the copy or keep both under a new name; whatever is not kept goes to Trash.
|
||
`pdfs conflicts` and `pdfs conflicts resolve <path> --keep original|copy|both` do the same from
|
||
the CLI. Files that are open or still uploading are refused until they settle.
|
||
- **Pause one folder.** A mirrored folder on the Sync page has its own pause button; while paused
|
||
it is not reconciled in either direction and its mode cannot be switched, and resuming syncs it
|
||
straight away. `pdfs sync pause --folder <id>` / `pdfs sync resume --folder <id>` do the same, and
|
||
`pdfs sync list` marks paused folders. The pause is kept across restarts. On-demand folders
|
||
upload through the shared queue, so only the global pause holds them back.
|
||
- **Bandwidth limits.** Preferences → General → Network caps upload and download speed in MiB/s,
|
||
shared by every transfer and applied to running ones from their next block; downloads include
|
||
reads through the mount. `pdfs sync limit --up 2M --down 0` does the same, and `pdfs sync limit`
|
||
on its own prints the current caps. The caps are kept in `config.json` across restarts.
|
||
- **Tray states and menu.** The tray icon shows whether the drive is synced, syncing, paused,
|
||
offline or needs attention. It asks the panel for attention while changes keep failing, and
|
||
its tooltip gives the same line as the menu. A left click opens the app. The menu gains
|
||
"N issues — View", which opens the Sync page, and "Sign In…" when signed out. "Hide Tray Icon"
|
||
is remembered across logins (`tray_hidden` in `config.json`), and Preferences → Appearance
|
||
→ Show tray icon brings the icon back. "Stop Proton Drive…" asks for confirmation in the app,
|
||
then stops the service and the tray. `pdfs-app` accepts `--page <name>` and `--confirm-stop`.
|
||
A second launch raises the running window instead of opening another one.
|
||
- **Stop sharing.** Shared by Me → item menu → "Stop Sharing…" and `pdfs unshare <path>` (with no
|
||
id) remove the public link, every invitation and every member in one go. Each step is tried even
|
||
if an earlier one fails, and a partial result names what is still shared.
|
||
- **Restore another computer's folders.** Every computer's menu on the Computers page has
|
||
"Restore to This Computer…", which syncs that computer's backed-up folders down to paths you
|
||
pick; they keep syncing with that computer's copy, and this machine keeps its own identity.
|
||
This computer's own restore moved from the header into its row's menu. `pdfs sync restore
|
||
--device <uid>` does the same from the CLI.
|
||
- **Public link expiry.** The Share dialog's create-link form has an "Expires" choice (Never,
|
||
1 day, 7 days, 30 days), and an existing link shows when it expires or that it has expired.
|
||
|
||
### Changed
|
||
- **New app shell.** Every page has its own header bar; the sidebar ends in a footer with sync
|
||
state, storage and the account; the Settings page became a Preferences dialog (Ctrl+,), with
|
||
transfers on the Sync page and developer details in About. The app follows the system accent
|
||
colour; the Proton purple accent is a preference.
|
||
- **My Files header.** New folder and both uploads sit in one **New** menu; the view button switches
|
||
grid and list, and its menu sorts by name, size or date, reversed or not, folders first or not.
|
||
The choice is remembered. Build Thumbnails and Open in File Manager moved to the folder menu.
|
||
- **Browsing history.** Back and forward buttons (Alt+Left/Right, mouse back/forward) replace the
|
||
single Up button; Alt+Up goes to the parent folder. Back leaves a search first.
|
||
- **Move to…** picks the destination from a folder browser instead of a typed path, and works on a
|
||
whole selection. Rename selects the name without its extension.
|
||
- **Drop to upload.** Files and folders dropped on My Files upload into the folder on screen. The
|
||
upload toast links to the Sync page.
|
||
- **Photos.** The main view is called Timeline. Right-clicking a photo opens a menu to open it,
|
||
favourite it, select it or move it to Trash; Ctrl+A selects every loaded photo. An empty filter
|
||
says what it filtered ("No videos in June 2024") instead of the first-run hint. A banner shows
|
||
while a Takeout import runs and links to it. Pages load 200 photos at a time and keep loading
|
||
until the window is full.
|
||
- **Trash.** Select several items (click, Shift/Ctrl+click, Ctrl+A) to restore or permanently
|
||
delete them together; the permanent delete still asks first, with the count.
|
||
- **Shared by Me.** A row opens the item. Copy link stays one click away; Open, Show in My Files,
|
||
Open Link and Manage Access… are in the row's menu.
|
||
- **Computers.** Rename, Use This Computer's Identity and Remove are in one menu per computer.
|
||
Removing a computer deletes its backups, so the dialog now asks for its name to be typed. The
|
||
page says which computer this machine backs up as, and says "computer" throughout.
|
||
- **Sign in.** Failures read as sentences — wrong password, too many attempts, no connection, no
|
||
keyring, a skipped two-factor or verification step — instead of the raw error. The button spins
|
||
while signing in, and the page links to account creation and password reset. The two-factor
|
||
dialog mentions recovery codes.
|
||
- **Real menus.** Right-click menus in My Files, Photos, Shared by Me and Computers are GTK menus:
|
||
arrow keys, Enter and Escape work, and the Menu key or Shift+F10 opens the one for the selection
|
||
in My Files. Items follow one order — Open, Open With…, Play · Available offline · Share…,
|
||
Copy Link · Rename…, Move To…, Versions… · Move to Trash. Right-clicking empty space offers New
|
||
Folder, the uploads, Select All, Refresh and Open in File Manager. Open With… asks which
|
||
application to use.
|
||
- **One selection in My Files.** The grid and the list share their selection, so switching views
|
||
keeps what is selected, and dragging across empty space draws a selection box in both.
|
||
- **Details pane.** On a window at least 1000 px wide the details pane docks beside the files
|
||
instead of covering them. It has Info, Offline, Sharing and Versions sections: who has access or
|
||
whether there is a public link, how many versions there are, and Share…, Copy Link and
|
||
Versions… buttons.
|
||
- **Search in this folder.** Inside a folder, a folder button beside the search box limits the search
|
||
to that folder and everything below it. `pdfs search --in <folder>` does the same from the CLI.
|
||
In the list view a Location column shows which folder each hit is in. Pinned files listed by the
|
||
search prompt and `pdfs-query` show whether they are on disk, instead of always showing them as
|
||
not downloaded.
|
||
- **Album management.** The Albums view has a New Album button, and an album's right-click menu
|
||
offers Rename… and Delete Album…. Deleting an album keeps its photos in the timeline. A photo's
|
||
menu and the selection bar offer Add to Album…, and inside an album, Remove from Album takes a
|
||
photo out without trashing it. Albums shared with you only open. The CLI gains `pdfs new-album`,
|
||
`rename-album`, `delete-album`, `add-to-album` and `remove-from-album`. This needs
|
||
proton-drive-rs 0.6.6.
|
||
- **Albums view fixes.** The timeline's filter row no longer shows over the album grid. The header
|
||
counts albums. Back from an album returns to the grid where you left it instead of reloading it,
|
||
and going from Albums back to Timeline keeps the timeline's scroll position unless an album was
|
||
opened in between.
|
||
- **Photo viewer rewrite.** The lightbox's controls fade out after two seconds without pointer
|
||
movement. Photos zoom with Ctrl+scroll, a pinch, `+`/`-`, `1` for 100 % and double-click, and
|
||
pan by dragging; `0` fits them again. The details panel docks beside the photo instead of 120 ms
|
||
covering it. Stepping past the last loaded photo loads the next page, and the counter shows the
|
||
library's total rather than the loaded count. A video plays inline. Save a Copy uses the photo's
|
||
real name and reports failures, and Open With… offers the app chooser.
|
||
- **Photos scroll without a Load More button.** The timeline loads the next page while you are
|
||
still a screen and a half from the end, with a spinner under it while a page is on its way. A
|
||
month scrubber on the right edge of the whole timeline, marked by year, jumps to the month you
|
||
drag it to and follows the scroll otherwise.
|
||
- **Activity log redesign.** The feed is grouped by day (Today, Yesterday, weekday, date), with
|
||
a time column and filter chips for All, Transfers, Changes, Sharing and Problems. A conflict the
|
||
daemon logs again on every run now shows once, with a count. A "Needs attention" section on top
|
||
lists the conflicts still waiting for a decision. Each logged conflict has a Resolve… button that
|
||
opens the same dialog as the Sync page, and reads as resolved once the copy is gone.
|
||
- **Share dialog.** The Invite button stays off until every address looks like an email; a
|
||
malformed one marks the field red and names the entry in a tooltip. Buttons show a spinner
|
||
while their request runs, so a slow invite or link removal can't be sent twice.
|
||
- **New app icon.** A white cloud with the download arrow cut out of it, on a rounded Proton
|
||
purple tile with a darker lower edge, drawn to the GNOME app-icon grid. It stays legible down to
|
||
16 px on light and dark panels.
|
||
- **Shared with Me redesign.** Only sections with something in them are shown, and a page
|
||
with nothing shared says so in one place. Pending invitations sit at the top, with an Accept
|
||
button beside each. A row reads as the name over who shared it, when, and its size; what the
|
||
share allows is a tinted pill ("Can view", "Can edit", "Can manage") rather than a bare
|
||
role word that looked like a button. Open, Show in My Files and Leave… are in the row's ⋮
|
||
menu, and folders show a chevron. Inside a shared folder the header shows the path from the
|
||
share down, with a back button. Add Bookmark is an icon in the header.
|
||
- **US spelling throughout.** The app, the CLI and the docs say "Favorite" and "color", matching the
|
||
`pdfs favorite` command.
|
||
|
||
## [1.11.1] — 2026-09-19
|
||
|
||
Schema: **31** (unchanged).
|
||
|
||
### Fixed
|
||
- **Double-click opens folders reliably.** Selecting a file no longer slides the details pane in
|
||
by itself: the pane moved the files under the pointer between the two clicks of a double-click,
|
||
so the folder did not open. The pane now shows on request, from the header's Details toggle,
|
||
Alt+Enter or the item menu's Details, and then follows the selection. Closing it keeps the
|
||
selection.
|
||
- **The details pane opens only when asked.** On a wide window it used to show at start, empty,
|
||
and close again whenever you entered a folder. It now follows the header toggle alone. With
|
||
nothing selected it says so instead of closing, so opening a folder no longer moves the files.
|
||
- **Context menus no longer have wide margins under some themes.** A menu opened on a file tile
|
||
was placed inside the tile, so a theme's rules for tile contents (Catppuccin's
|
||
`gridview > child box { margin: 12px }`) spaced out every section.
|
||
- **My Files behaves more like a file manager.** A click on empty space between the files clears
|
||
the selection; Ctrl or Shift held keeps it. Backspace goes back while the focus is on the files.
|
||
- **An upload parked behind a temp-file rename no longer stays parked forever.** A create for a
|
||
name that looks like a writer's temporary file waits for the rename to the final name. If that
|
||
rename never came — the writer crashed, the file was deleted, or the name was never temporary —
|
||
the upload sat in the queue permanently, holding its staged bytes. The queue now sweeps parked
|
||
creates while it is idle: one whose file is gone is dropped, and one that has been parked for
|
||
over an hour with nobody holding it open is uploaded under the name it has. An open file is
|
||
left alone however old it is (B94).
|
||
- **A file whose real name ends in `.tmp` uploads normally.** `.tmp` and `.temp` were treated as
|
||
writer temp-file suffixes, so ordinary files — a Google Photos Takeout `index.tmp`, for
|
||
instance — were parked waiting for a rename that was never going to happen (B95).
|
||
- **Staged bytes that belong to nothing are cleaned up.** A staged blob that matches no queued
|
||
upload and names no node used to be kept forever and warned about once per blob on every mount.
|
||
Blobs older than 30 days are now retired with their size logged; younger ones are kept, but
|
||
reported as a single line instead of one per blob (B96).
|
||
- **Refreshing the gallery repeatedly no longer wedges the control socket.** Each refresh scope
|
||
now runs one at a time; asking again while one is running is answered immediately rather than
|
||
repeating work that contends with it for the database. `pdfs diagnostics` also names the scope
|
||
of a running refresh (B97).
|
||
- **Shutting down during a photo refresh no longer logs a wall of "undecryptable photo"
|
||
warnings.** The metadata pass stops when teardown begins instead of running into the cancelled
|
||
runtime. Nothing was wrong with those photos; the next refresh reads them again (B97).
|
||
|
||
## [1.11.0] — 2026-09-19
|
||
|
||
Schema: **31** (V30 `photos.content_hash` / `main_uid` / `group_key`, V31 `photos.resolved_at`); SDK bumped to `proton-sdk` /
|
||
`proton-drive-rs` **0.6.5**.
|
||
|
||
### Changed
|
||
- **The gallery lays photos out in justified rows.** Every tile is now its own photo's shape:
|
||
photos are taken in capture order until they no longer fit the target height, and the row is
|
||
then scaled to land exactly on the right margin. Nothing is cropped and nothing is
|
||
letterboxed, which is what the square centre-cropped tiles used to do to portraits and
|
||
panoramas. The last row of a day stays at the target height rather than being stretched, and
|
||
Ctrl+scroll / Ctrl+± now changes that target height. Gaps and tile corners are smaller, so the
|
||
page reads as photos rather than as cards.
|
||
- **Thumbnails stay on screen while you scroll.** A decoded thumbnail now lands in the texture
|
||
cache even when the tile that asked for it has already been recycled, the cache is a real LRU
|
||
holding 1500 textures instead of 600, and the rows just past the edge of the window are
|
||
fetched ahead of the scroll direction. Scrolling fast through a large timeline no longer
|
||
leaves a screen of blank tiles behind.
|
||
- **Refresh on the Photos page really refreshes.** Instead of only clearing the freshness stamp
|
||
and letting the next read serve the stale page, it starts the timeline re-read and follows it:
|
||
the page paints from what the daemon holds, then paints again when the new timeline lands. The
|
||
timeline is also considered stale after 60 s rather than 5 minutes. `pdfs refresh photos`
|
||
blocks until the re-read is done, as it reads.
|
||
|
||
- **Restore puts back the whole tree.** The trash is a flat list, but you delete a shape: a
|
||
folder, and often things you had already deleted inside it. Restoring a folder now also
|
||
restores the items that were trashed inside it, and restoring one of those items first
|
||
restores the trashed folders above it — otherwise the file came back into a folder that is
|
||
still in the trash, which is to say nowhere you can see it. Parents are restored before their
|
||
children, and each node is still restored individually, so one node the server refuses does
|
||
not cancel the rest.
|
||
|
||
### Added
|
||
- **A RAW and its JPEG are one photo.** A camera that writes both put two tiles of the same shot
|
||
in the timeline; now it puts one, badged `RAW`, and the lightbox has a control that switches
|
||
between the files. The Raw tab still lists every raw file individually, because that is what
|
||
the tab is for. Grouping follows the server's own photo relation where it has one — which
|
||
also collapses live photos and bursts — and otherwise pairs files with the same name taken on
|
||
the same day where one is raw and the other is not. Deleting the tile deletes the whole shot,
|
||
and the confirmation says how many files that is.
|
||
- **Delete from the gallery.** Select photos with the Select button, with Ctrl+click or with
|
||
Shift+click, then Move to Trash — or press Delete in the lightbox. The tiles leave the grid
|
||
immediately with an Undo toast, and the photos land in Proton's trash, where they stay
|
||
recoverable. A photo the server refuses to trash comes back into the grid.
|
||
- **A photo deleted on your phone disappears here.** The daemon now follows the photos volume's
|
||
own event stream, so a photo trashed on another device leaves the gallery within about ten
|
||
seconds instead of waiting for the timeline to go stale.
|
||
|
||
- **`pdfs diagnostics`.** Reports what the daemon is doing right now: every worker thread with the job
|
||
it holds and for how long, queue depths per lane, the control requests still in flight, resident
|
||
size and the pending-op count. Answers even while the daemon is wedged, which is the only time
|
||
anyone asks (B91).
|
||
- **A stall watchdog.** The daemon warns in the journal when a worker has held one job for two
|
||
minutes, when a control request has run that long, or when a queue is not draining — and it now
|
||
answers the systemd watchdog, but only after a real round trip over its own control socket. A
|
||
daemon that stops answering is restarted instead of sitting there looking healthy (B91).
|
||
|
||
### Fixed
|
||
- **A Pixel raw capture groups with its JPEG.** Pixel names the two files of one shot
|
||
`PXL_….RAW-01.COVER.jpg` and `PXL_….RAW-02.ORIGINAL.dng`, so their stems differ and the pairing
|
||
rule never saw them as one shot. The part-of-a-shot marker is now cut off before the names are
|
||
compared, and the grid shows the cover JPEG once. The grouping is computed by a timeline
|
||
refresh, so an existing library pairs up after the next one.
|
||
- **The Raw tab no longer decodes a raw it does not have to.** A raw file that was written next
|
||
to a JPEG of the same shot is now given that JPEG's thumbnail, which the server usually already
|
||
has. No raw download and no raw decode for those tiles.
|
||
- **Photos can be moved to Trash from the gallery again.** Photos live on the photo volume, which
|
||
the mount never holds, so the write check found no node to consult and refused every photo with
|
||
"trash access: not allowed". Membership of our own timeline now answers for them; an album photo
|
||
on someone else's volume is still refused.
|
||
- **A photos refresh no longer fails with "Resource temporarily unavailable" (B92).** Re-reading a
|
||
twelve-thousand-photo library resolves the nodes in batches of 200 and takes minutes, but the
|
||
daemon held the control reply until it had finished — well past the client's 120 s read bound.
|
||
The caller therefore saw `io: Resource temporarily unavailable (os error 11)` for a refresh that
|
||
was running perfectly, and the gallery reloaded from the old timeline. The daemon now answers as
|
||
soon as the refresh is running, and the new `PhotosRefreshStatus` request reports when it is
|
||
done. Each refresh also logs its photo count, batch count and duration.
|
||
- **A worker no longer panics during shutdown (B91).** The FUSE worker pool is joined before the
|
||
tokio runtime is dropped, so a worker still inside a job does not find a runtime that is going
|
||
away underneath it.
|
||
- **A permanently rejected block upload is no longer retried (SDK 0.6.5).** The SDK now knows
|
||
which API errors are worth replaying: 4xx is permanent, except 404, 408 and 429.
|
||
- **Long downloads no longer fail on expired block URLs (SDK 0.6.5).** A download whose block
|
||
URLs lapse mid-transfer re-mints them once instead of failing, on the whole-file path and on
|
||
public links, as reads already did.
|
||
- **A long rate-limit answer can no longer look like a hang (SDK 0.6.5).** A server-supplied
|
||
`Retry-After` is now capped at 120 s.
|
||
|
||
## [1.10.2] — 2026-09-16
|
||
|
||
Fixes a daemon that could not be stopped or restarted. No schema change.
|
||
Schema: **28**; no SDK change.
|
||
|
||
### Fixed
|
||
- **A dead network mount in your home directory no longer wedges the daemon (B90).** The
|
||
"This computer" file indexer walked `$HOME` and descended into other FUSE mounts — an sshfs
|
||
whose server had gone away, an unresponsive rclone. Stat'ing one of those blocks
|
||
uninterruptibly, so the indexer thread stopped responding to SIGKILL, the daemon could never
|
||
exit, `systemctl --user stop` timed out, and the half-dead process kept the cache database lock
|
||
that the next start needed: every restart then failed with "another Proton Drive daemon is
|
||
already using cache.db". The indexer now skips mountpoints below your home directory by path,
|
||
without touching them. Nothing changes about which files are indexed — those mounts were always
|
||
excluded from the results, just not before they had been stat'ed.
|
||
|
||
## [1.10.1] — 2026-09-15
|
||
|
||
Dependency upgrade release, clearing both open Dependabot advisories. No schema change.
|
||
Schema: **28**; SDK bumped to `proton-sdk` / `proton-drive-rs` **0.6.4**.
|
||
|
||
### Security
|
||
- **`lru` IterMut Stacked Borrows advisory.** The SDK now depends on `lru` 0.18.
|
||
- **`atty` unaligned read advisory.** The tray moved to `ksni` 0.3, which no longer pulls
|
||
`dbus-codegen` → `clap` 2 → `atty`.
|
||
|
||
### Changed
|
||
- **Keyring backend is `keyring-core` + `dbus-secret-service-keyring-store`** (keyring 4's split).
|
||
Same libdbus Secret Service as before; stored sessions written by earlier releases are still
|
||
found, so no re-login is needed.
|
||
- **The tray waits for a late StatusNotifierWatcher** instead of failing to start when it beats
|
||
the desktop's tray host onto the session bus.
|
||
- Upgraded `gtk4` 0.11, `libadwaita` 0.9, `webkit6` 0.6, `rusqlite` 0.40 (bundled SQLite),
|
||
`fuser` 0.18, `directories` 6, `sha1`/`sha2` 0.11, `base64` 0.23.
|
||
|
||
## [1.10.0] — 2026-09-15
|
||
|
||
One-request move-and-rename in the drain, and who shared an item with you. No schema change.
|
||
Schema: **28**; SDK bumped to `proton-sdk` / `proton-drive-rs` **0.6.3**.
|
||
|
||
### Added
|
||
- **Shared with me says who shared an item, and when.** The GUI row subtitle and `pdfs
|
||
shared-with-me` show the inviter and invite date for a share root. The invitation's signature is
|
||
checked against the claimed inviter's keys; when it does not verify, the GUI shows a warning
|
||
and the CLI prints one, since the sender may be forged. `DirEntry` gains `shared_by`,
|
||
`shared_at` and `shared_by_unverified` (defaulted, so older front-ends and daemons interoperate).
|
||
|
||
### Changed
|
||
- **A queued move-and-rename lands as one request.** `drain_rename` used to rename in the source
|
||
folder and then move, with a rename-out-of-the-way step when the destination held the name. It
|
||
now calls `move-multiple` with a target name, so there is no half-applied state between the two
|
||
halves and no second call for the encrypted-name requirements to go stale against (B46). A
|
||
rename that stays in the same folder still uses the rename endpoint.
|
||
|
||
### Fixed
|
||
- **Moving a node with no signature email no longer fails.** Such nodes (created anonymously, e.g.
|
||
through a public link) need the passphrase signed by the mover; SDK 0.6.3 sends that signature.
|
||
- **A node trashed through the newer API field is recognised as trashed.** The SDK now reads
|
||
`TrashTime` as well as the legacy `Trashed` field, so the drain no longer tries to rename a node
|
||
that is already in the trash.
|
||
|
||
## [1.9.3] — 2026-09-01
|
||
|
||
The daemon's wait-for-login loop, and the latency that fixing it introduced. No schema change.
|
||
Schema: **28**; SDK unchanged at `proton-sdk` / `proton-drive-rs` **0.6.2**.
|
||
|
||
### Fixed
|
||
- **An enabled service with no saved session no longer polls the keyring 28,800 times a day.**
|
||
`wait_for_session` slept a flat 3 s between checks, and a check is not just a sleep — `auth::load`
|
||
builds a fresh `keyring::Entry`, so every iteration opened a new D-Bus connection to the Secret
|
||
Service and negotiated a session on it. The wait now backs off, and "not logged in" is said once
|
||
at `info` instead of on every pass, which also stops the journal filling with tens of thousands
|
||
of identical lines (`#21`).
|
||
- **Signing in wakes the parked daemon instead of leaving the mount up to an interval late.** The
|
||
backoff had no wake-up path: a unit started at boot reaches the ceiling within ~90 s, and
|
||
`enable --now` is a no-op on an already-active unit, so the GUI login and the tray's Connect left
|
||
it sleeping. `service::enable_start` now runs `enable` + `restart`, which interrupts the sleep and
|
||
still starts a unit that is down, and `pdfs login` restarts the unit when it is already active —
|
||
deliberately without touching `enable`, so a headless setup driving `pdfs daemon` by hand is left
|
||
alone. The ceiling also drops from 60 s to 15 s, still an 80% cut in keyring/D-Bus wakeups
|
||
(28,800/day → 5,760/day), bounding the worst case for paths that never reach systemd.
|
||
|
||
### Changed
|
||
- The wait is extracted into `wait_for_session()`, taking the load and sleep calls as parameters,
|
||
so the schedule is testable without a keyring and without real time passing.
|
||
|
||
## [1.9.2] — 2026-08-28
|
||
|
||
Repair for photos a Takeout import filed under the day they were imported, an `fzf` launcher front
|
||
end, and a gallery that survives 1,600 photos on one date. No schema change. Schema: **28**; SDK
|
||
`proton-sdk` / `proton-drive-rs` **0.6.2**.
|
||
|
||
### Added
|
||
- **`pdfs redate-photos`: repair photos filed under the day they were imported.** A Takeout whose
|
||
metadata sidecars did not match imports with no capture time at all, and the SDK then seals
|
||
"now" into every revision — a decade of photos lands on one date. The date is still in the file
|
||
name, so this finds photos whose name disagrees with their stored capture time by more than a
|
||
day and rewrites them: download, re-upload at the right time, carry favourites and album
|
||
memberships across, then **trash** (never delete) the original — Proton has no API for editing a
|
||
sealed capture time. `--dry-run` reports without touching anything, `--from`/`--to` aim the run
|
||
at the days an import landed on, and `--limit` caps it. Followed with `pdfs redate-status`,
|
||
stopped with `pdfs cancel-redate`.
|
||
|
||
- **`pdfs-prompt --fzf`: launcher search that updates as you type.** A dmenu-style launcher
|
||
(fuzzel, rofi, wofi, …) is handed one static list and filters it itself, which is why `--dmenu`
|
||
has to make searching two steps — type, Enter, *then* results. `fzf` can re-run a command on
|
||
every keystroke, so the new mode re-queries the daemon per keystroke (debounced to match the GTK
|
||
prompt) and keeps `--disabled` on so the daemon's ranking, not fzf's matcher, decides the order.
|
||
Abbreviation and typo tolerance therefore work here exactly as they do in the built-in window.
|
||
- Started without a tty — from a WM keybinding — `--fzf` spawns a terminal for itself (foot,
|
||
ghostty, kitty, alacritty, wezterm or xterm, whichever is installed), each with the app-id/class
|
||
`pdfs-prompt` so a single window rule floats it. Override with `"prompt": { "terminal": [...] }`,
|
||
where a `{cmd}` token places the command and its absence appends it. `"prompt": { "mode": "fzf" }`
|
||
makes it the default front end for an existing keybinding.
|
||
|
||
### Fixed
|
||
- **Google Photos Takeout: sidecars with a localized suffix are matched again.** Google localizes
|
||
the `.supplemental-metadata` it appends to a photo's JSON sidecar — a German export writes
|
||
`.ergänzende-Metadaten` — and the scanner only knew the English spelling. No sidecar matched, so
|
||
no photo had a capture time, so every photo in the export was stamped with the moment it was
|
||
uploaded. Matching now hangs on the photo name in front of the suffix rather than on the suffix
|
||
itself, which is locale-agnostic, and covers the misspelt variants Google has also shipped.
|
||
- **A photo with no usable sidecar now takes its date from its file name.** `PXL_20260818_171030868.jpg`,
|
||
`IMG_20230219_171030.jpg`, `Screenshot_20230101-102950.png`, `IMG-20230219-WA0001.jpg` and
|
||
`Screenshot from 2023-01-01 10-29-50.png` all carry the date the photo was taken. Read as UTC, so
|
||
it can be hours out — but never the years out that "the moment it was imported" is. A sidecar
|
||
still wins.
|
||
|
||
- **An opened file or folder no longer dies with the window that opened it.** Everything launched
|
||
through `open_with` was started in the caller's process group with the caller's stdio, which was
|
||
invisible from a resident GUI but fatal from `--fzf`: the prompt exits the moment you choose
|
||
something, its terminal closes, and the pty hangup killed the application it had just started —
|
||
so an `@dir` rule like `alacritty --working-directory {}` opened nothing at all. Opened children
|
||
now get their own process group and no inherited stdio. Affects every front end; only `--fzf`
|
||
could observe the bug.
|
||
|
||
### Changed
|
||
- **The gallery virtualises at the row, not the day.** The Photos ListView had one item per day,
|
||
and a bind built every one of that day's tiles — fine for a normal day, ruinous for the ~1,600
|
||
photos a Takeout import drops onto a single date, which was 1,600 widgets constructed on the main
|
||
thread before the section could be shown. Days are now flattened into heading rows and grid rows,
|
||
so a bind is a handful of widgets whatever the day holds. A resize or a zoom step rebuilds the row
|
||
model (the column count decides which photos share a row) and scrolls back to the photo that was
|
||
at the top.
|
||
- Updated to `proton-sdk` / `proton-drive-rs` 0.6.2 (photos save-to-timeline, `enumerate_events`).
|
||
|
||
- The daemon side of both launcher front ends — search, pins, mountpoint lookup and the
|
||
open-a-result policy — moved out of `pdfs-gui/src/dmenu.rs` into a shared `pdfs-gui/src/query.rs`,
|
||
so `--dmenu` and `--fzf` cannot drift on what a result set is or what "open" means. No behaviour
|
||
change to `--dmenu`.
|
||
|
||
## [1.9.1] — 2026-08-21
|
||
|
||
Thumbnails everywhere the GUI lists files, a status bar under the Files browser, and the
|
||
repository's contribution/branding files. No schema change. Schema: **28**; SDK unchanged at
|
||
`proton-sdk` / `proton-drive-rs` **0.6.1**.
|
||
|
||
`exiftool` (`libimage-exiftool-perl` / `perl-image-exiftool` / `perl-Image-ExifTool`) is a new
|
||
runtime dependency, declared in the `.deb`, `.rpm` and `PKGBUILD` packaging. It is only used to
|
||
pull the embedded preview out of a camera RAW; without it every other image format still gets a
|
||
thumbnail.
|
||
|
||
### Added
|
||
- **Thumbnails for ordinary Drive files**, not just the Photos timeline — painted in Files (both
|
||
the grid and the Name column of the list), Shared, Shared by me, and Trash. Standard raster
|
||
formats (`bmp`, `gif`, `jpeg`, `png`, `tiff`, `webp`) are decoded in the daemon; camera RAWs
|
||
(`arw`, `cr2`, `cr3`, `dng`, `nef`, `orf`, `raf`, `rw2`, and the rest of the concrete list) are
|
||
served from their embedded preview via `exiftool`. Previews are cached beside the existing photo
|
||
thumbnails, outside the cache-budget scan, and tagged with the node's modification time — so a
|
||
new revision of an image can never be shown with its predecessor's thumbnail.
|
||
- Thumbnails for visible rows are requested opportunistically as tiles scroll in, and the daemon
|
||
drops work for listings the user has already navigated away from. The listing generation is
|
||
issued by the daemon (`ReserveFileThumbGeneration`), not seeded from the front-end's clock.
|
||
- **Build a whole folder tree ahead of time** from the Files toolbar: one recursive local-thumbnail
|
||
job per daemon, with live scan/found/completed/failed progress and a Cancel that appears only
|
||
once the daemon has acknowledged the start. The job outlives navigation — it is deliberate work,
|
||
separate from the opportunistic per-tile requests.
|
||
- **A Dolphin-style status bar under the Files browser**: the current listing's counts, a Zoom
|
||
slider for the icon grid, and account storage usage. Zoom resizes the realised grid cells rather
|
||
than rebuilding the model, so dragging the slider does not tear down the selection; the default
|
||
size reproduces the previous fixed 72 px tiles. Capacity stays out of the bar until a real
|
||
reading lands, and the quota fetch (60 s TTL) now also runs while Files is on screen instead of 120 ms
|
||
only in Settings.
|
||
- Repository housekeeping: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, issue and pull
|
||
request templates, a project logo, and workspace crate metadata (`repository`, `homepage`,
|
||
`readme`, `keywords`, `categories`).
|
||
|
||
### Fixed
|
||
- A remote thumbnail miss no longer suppresses local generation. The two negative results are now
|
||
held in separate bounded caches: "Proton has no thumbnail for this node" and "this exact revision
|
||
was decoded locally and could not be read" mean different things, and only the second is a
|
||
permanent verdict for that revision.
|
||
- A RAW whose preview could not be extracted because of the *environment* — `exiftool` missing, a
|
||
failed download — is retried instead of being remembered as undecodable. Only bytes conclusively
|
||
outside the supported formats are cached as a negative.
|
||
- A recursive build that finds a uid an opportunistic tile job already owns waits briefly for it
|
||
rather than either duplicating the work or blocking on it forever, and a cancellation racing a
|
||
waiter's registration is caught by a backstop instead of leaving the waiter parked.
|
||
- Starting a build for a root that is already building attaches to that job; starting one for a
|
||
*different* root is refused explicitly rather than silently reporting the running job's progress
|
||
as the caller's.
|
||
- Decrypted camera RAWs are staged for `exiftool` in an owner-only (`0700`) directory of their own,
|
||
removed as soon as the preview is out and swept at cache open after an unclean shutdown.
|
||
- The RAW extension list had `rw` (not a format) and was missing `raw`; both classification helpers
|
||
now read from one shared table instead of two hand-maintained `matches!` arms.
|
||
- The tray and desktop-entry icons were wrong/missing; `.desktop` files and the packaged SVG were
|
||
corrected.
|
||
|
||
## [1.9.0] — 2026-08-18
|
||
|
||
Google Photos import, and a pass over the desktop app's interaction gaps. No schema change.
|
||
Schema: **28**. Builds against the published `proton-sdk` / `proton-drive-rs` **0.6.1** — the album
|
||
writes this release needs are in the registry crates, so the local `[patch.crates-io]` shim the
|
||
feature was developed against is gone and no local path dependency remains.
|
||
|
||
### Added
|
||
- **Import a Google Photos Takeout export into Proton Photos** — `pdfs import-google-photos
|
||
takeout-*.zip` (add `--dry-run` to see what it would do, `--wait` to follow it), or the import
|
||
button in the Photos header, which multi-selects the export's zips. Albums in the export are
|
||
recreated as Proton albums and their photos filed into them; year folders and `Archive/` import
|
||
into the timeline; `Trash/` is skipped. Capture times and favourites come from Google's per-photo
|
||
JSON sidecars, including the truncated and `(N)`-swapped sidecar names Google produces.
|
||
- Photos already on the account are **not re-uploaded**: the import asks the photos volume which of
|
||
the export's names exist (batched, 150 per request), reads and SHA-1s only those files, and skips
|
||
the ones whose content digest matches too — Proton's own duplicate rule. A library already partly
|
||
synced by Proton Photos on a phone therefore imports only what is missing, and an interrupted
|
||
import resumes by being run again. Google keeps a full copy of a photo in every album folder it
|
||
appears in; those fold into one upload that joins several albums.
|
||
- The import streams each photo straight out of its zip, so an export needs no scratch space to
|
||
unpack into, and reports as a job in `pdfs transfers` and the GUI's activity view. `pdfs
|
||
import-status` shows the counts, `pdfs cancel-import` stops it — a cancelled run still files what
|
||
it uploaded into its albums.
|
||
- **An Import page in the GUI** (Settings → *Import from Google Photos*, or the import button in
|
||
the Photos header). Archives are staged by drag-and-drop onto the page or through the file
|
||
chooser, listed with their sizes so the set can be checked before anything is sent, and launched
|
||
as either a Scan (dry run) or a confirmed Import. The page shows live progress off the import's
|
||
job, keeps the finished run's report on screen, and can stop a run in flight; because the import
|
||
outlives the page, it is polled wherever the user navigates and its completion is reported as a
|
||
desktop notification.
|
||
|
||
- SDK (`proton-sdk-rs` **0.6.1**): album **writes** — `ProtonPhotosClient::create_album` and
|
||
`add_photos_to_album`, ported from the TypeScript SDK (upstream C# has no album write API), plus
|
||
batch duplicate detection (`find_duplicates_many`, `name_collisions`). The workspace deps moved
|
||
`"0.6.0"` → `"0.6.1"` and `Cargo.lock` re-resolved to the registry crates. Adding a photo from an
|
||
album *shared with us* (a different volume) is still unported and fails that photo's outcome.
|
||
- **Multi-select in Files.** Both views select a batch (click-drag, Ctrl-click, Shift-click); a bar
|
||
slides in reporting the count and offering Move to Trash and offline on/off for the whole
|
||
selection. Delete acts on everything selected, Esc clears it, and right-clicking inside a
|
||
selection offers the batch actions rather than the one row under the pointer. Actions that need a
|
||
single subject — rename, move, share, revisions — stay single-entry, and the details pane steps
|
||
aside for a batch instead of speaking for it.
|
||
- **Undo on trash.** "Moved *X* to Trash" now carries an Undo that restores the batch, single files
|
||
included.
|
||
- **The empty states are a place to act**: an empty folder offers Upload files / New folder, and an
|
||
empty photo timeline offers Upload photos / Import from Google Photos, instead of describing what
|
||
the user could go and do elsewhere.
|
||
- **Ctrl+F works from any page**, navigating to Files and focusing the search box, rather than being
|
||
silently inert everywhere but Files.
|
||
- Staged Takeout archives survive closing the window; archives that have since been moved or deleted
|
||
are dropped from the restored set.
|
||
|
||
### Fixed
|
||
- Settings showed the local cache as "X of 0 B used" when no cache budget was set. An unset budget
|
||
means *unlimited*, so it now reads "X cached — no limit set" rather than implying a zero cap.
|
||
- The cache-budget editor fired a `SetCacheBudget` round-trip — and a toast — on every step, so a
|
||
run of `+` clicks applied a series of caps the user never asked for and stacked a column of
|
||
toasts. It now waits for the value to settle.
|
||
- A long pin list pushed everything below it off the end of Settings. It shows the first six with a
|
||
"Show all N" row for the rest.
|
||
|
||
## [1.8.2] — 2026-08-17
|
||
|
||
Closes the two findings 1.8.1's live verification produced (B85, B86) and finishes the data-safety
|
||
audit entries that had been sitting as "fixed in code, verification pending" since 1.0 (B49–B55).
|
||
No schema change. Schema: **28**.
|
||
|
||
The read path no longer assumes a revision is a series of 4 MiB blocks; it takes the geometry from
|
||
the revision. The B85 investigation also **contradicted its own bug report** — see Notes, because
|
||
the conclusion recorded there was that reads of such files were losing data, and they were not.
|
||
|
||
### Fixed
|
||
- A folder switched from mirror to on-demand now shows the files the mirror pass itself uploaded.
|
||
The sync engine created nodes on the remote without invalidating the parent's cached child
|
||
listing, and that listing is a database flag rather than an in-memory cache — so it survived a
|
||
daemon restart, and the files stayed invisible until the folder was switched back. Every remote
|
||
mutation the engine makes (folder create, file upload, and the three trash paths) now drops the
|
||
parent's cached listing by uid, which reaches a folder that no mount has resident — the case a
|
||
mirror folder always is (`docs/BUGS.md` B86).
|
||
- `pdfs ls` and `pdfs refresh` accept a path under any mount the daemon owns, not only the primary
|
||
one. Both previously answered "is not under the mountpoint" for anything inside a secondary
|
||
on-demand mount, which left a folder serving a stale listing with no manual way to re-enumerate
|
||
it. A path under nothing this daemon has mounted still gets the same error as before.
|
||
- A file whose blocks are not 4 MiB is no longer read twice over. Block offsets came from
|
||
`index * 4 MiB`, so on a revision laid out differently every block the client asked for
|
||
straddled two of the revision's own, and each was fetched and decrypted to answer for one — then
|
||
cached at boundaries the next read would not ask about. Offsets now come from the revision's
|
||
recorded block sizes, and the on-disk cache, the in-memory ring and the in-flight fetch key
|
||
address a block by the byte range it covers rather than by its index (`docs/BUGS.md` B85).
|
||
|
||
### Changed
|
||
- The block cache records each block's plaintext start alongside its `(mtime, size)` tag, and each
|
||
revision's block table in a small sidecar beside its blocks. Blocks cached by an earlier release
|
||
carry no start; they are honoured wherever the old 4 MiB assumption and the new plan agree on
|
||
the offset, which is every file this client uploaded — so the existing cache survives the
|
||
upgrade intact and is quietly refetched only for the files it was placing wrongly.
|
||
|
||
### Notes
|
||
- **B85's diagnosis was wrong, and the entry has been corrected rather than closed.** It read a
|
||
set of journal lines as proof that this client was requesting wrong byte ranges and being saved
|
||
only by B84's repair path. `RevisionReader::read_at` does not work that way: it plans over the
|
||
revision's real block sizes and clamps every block to the range asked for, identically in the
|
||
0.6.0 the client builds against and in every earlier version. A read can therefore never return
|
||
more bytes than it requested — yet every quoted line shows exactly that (`blen=4194304
|
||
got=4939506`), and the one place in the client that logs that shape can only reach it on the
|
||
opposite condition. The numbers were either transcribed with two fields swapped or captured from
|
||
a build not in the history; they want re-capturing from the journal before anything is concluded
|
||
from them. What was real is the assumption itself, and that is what this release removes — a
|
||
cost fix, not a correctness one. Nothing here changes what a read returns.
|
||
- The first read of a file still plans on the 4 MiB assumption, and learns the real geometry as a
|
||
side effect of opening the reader it needs anyway. Resolving a reader merely to *plan* would
|
||
reintroduce the per-read key derivation the block cache exists to avoid, so the first read of a
|
||
non-4-MiB file pays one straddling fetch and then self-corrects.
|
||
- Seven data-safety audit entries (B49–B55) moved from "fixed in code, verification pending" to
|
||
verified. The fixes were all genuinely present and needed no change; what they lacked was the
|
||
injected-failure testing each entry asked for, and that is what was written — unwritable
|
||
directories and `chmod 000` subtrees for permission failures, a real `/dev/shm`↔`/tmp` device
|
||
boundary for the cross-filesystem fallbacks, SQLite's `max_page_count` for a full disk, an abort
|
||
trigger for a failed insert, and truncated and unpublished sidecars for the crash boundaries.
|
||
Two seams were added to make failure arrangeable at all: `stage_write_at`, mirroring the
|
||
`preserve_write_at` that already existed, and `walk_local_tree` lifted out of the sync engine so
|
||
the local scan can be driven against a real tree without a daemon.
|
||
- **Two of those entries are not fully closed, and say so.** B51 and B52 concern crash consistency,
|
||
and nothing short of a machine actually losing power tests that; the crash *boundaries* are
|
||
covered by reconstructing on disk exactly what a crash at each point leaves. B54 also diverges
|
||
from its required fix on purpose: it has no "deliberate delete" confirmation path, so a total
|
||
local wipe is refused outright rather than offered for confirmation. That is stricter than asked
|
||
for, and the cost is that a user who really did delete everything must remove and re-add the
|
||
sync folder.
|
||
- B86's fix routes only `ls` and `refresh` to the owning mount. The other path-addressed control
|
||
requests — pin, rename, move, delete, sharing — still resolve against the primary mount only.
|
||
That is a larger change and was not what blocked the repro; it is recorded as an open follow-up
|
||
(`docs/BUGS.md` B88).
|
||
- Three findings were opened rather than fixed: the geometry above is learned one read late, so a
|
||
non-4-MiB file pays a single wasted fetch on its first read (B87); the remaining path-addressed
|
||
control requests are still primary-only (B88); and the container-level `CLAUDE.md` still
|
||
describes the SDK dependency as 0.1.11 when 1.7.1 bumped it to 0.6.0, which is what sent the
|
||
B85 investigation to the wrong source tree to begin with (B89).
|
||
|
||
## [1.8.1] — 2026-08-16
|
||
|
||
Some files read short through the mount and reported success — an application got a truncated
|
||
copy with nothing anywhere saying so (`docs/BUGS.md` B84). Found because GNOME's file indexer
|
||
kept complaining that PNGs under `~/ProtonDrive/Pictures` were corrupt; it was right. The rest
|
||
of this release is the long-standing architectural backlog from the July concurrency audits
|
||
(B25, B32, B39, B40, B43, B44), each verified against the current code before being fixed.
|
||
Schema: **28** — migration V28 adds `sync_entry.local_mtime_ns`.
|
||
|
||
### Fixed
|
||
- A read no longer trusts a revision whose recorded block sizes disagree with the file's size.
|
||
Such a file leaves the block/range path entirely: it is fetched through the whole-file,
|
||
manifest-verified download — the same path `pdfs pin` uses, and the reason pinning was the
|
||
workaround — and served from the resulting cached blob. One download per file however many
|
||
reads discover the problem at once, and files above 512 MiB fail `EIO` with a note to pin
|
||
them rather than pulling down gigabytes for one read.
|
||
- A block that comes back shorter than the file's size implies is never served, cached or kept
|
||
in memory. Assembling a read out of short blocks is what quietly produced the truncation:
|
||
the missing bytes were simply not appended, the kernel handed userspace a short read, and
|
||
every tool reads a short read as end-of-file.
|
||
- A synced folder no longer reports the same deletion as an error on every pass. Deleting a
|
||
local file that something else had already deleted counted as a failure, which kept its
|
||
baseline and made the next pass try again — forever. A path that is already gone is the
|
||
state the sync pass wanted (`docs/BUGS.md` B42).
|
||
- A mirror folder no longer misses an edit made in the same second as its last sync. Change
|
||
detection compared whole seconds, so rewriting a file in place without changing its length
|
||
read as "unchanged" and never uploaded — the remote silently stayed behind until something
|
||
else about the file moved. Baselines now record the modification time to nanoseconds
|
||
(`docs/BUGS.md` B25).
|
||
- A sync download no longer overwrites an edit that finished while it was running. The
|
||
destination is re-checked in the last moment before the download is published, and a local
|
||
file that has moved on since the pass planned for it is kept as a conflict copy — the same
|
||
resolution a both-sides-changed conflict gets (`docs/BUGS.md` B39).
|
||
- An upload interrupted by a writer no longer reports itself as synchronized. The baseline now
|
||
records the content that actually went up rather than whatever is on disk when the transfer
|
||
ends, so a revision torn by a concurrent write is replaced by a clean one on the next pass
|
||
instead of standing as the file's content on Drive (`docs/BUGS.md` B40).
|
||
- Listing a directory that changes while it is being read no longer skips or repeats entries.
|
||
`readdir` resumed by index into a listing rebuilt on every call, so a create or a trash
|
||
between two pages shifted everything after it. The listing is now frozen for the life of the
|
||
directory handle (`docs/BUGS.md` B43).
|
||
- Opening a file for writing while a previous close is still publishing no longer discards that
|
||
close's revision. The base is re-checked at the moment the write handle is installed, rather
|
||
than trusted across a copy that can be the size of the whole file (`docs/BUGS.md` B32).
|
||
- Unmounting now stops and joins the daemon's background workers instead of leaving them
|
||
running. The drain, sync engine, conflict sweep, online probe, local indexer and control
|
||
listener share a stop signal and interruptible waits, so an in-process remount no longer
|
||
starts a second set on top of the first (`docs/BUGS.md` B44).
|
||
|
||
### Notes
|
||
- The size `stat` reports was always the correct one; it is the reader's belief about where
|
||
blocks end that was wrong, so nothing about reported sizes changes.
|
||
- Verified live on the affected account: four multi-block PNGs that had been reading short now
|
||
deliver their full size, and the journal shows each one repaired. The diagnostics also
|
||
answered the open question, and not as expected — the revision metadata is fine; those files
|
||
simply do not have 4 MiB blocks, and this client assumes they do. Filed as `docs/BUGS.md`
|
||
B85. Until that is fixed, a file with non-4-MiB blocks is downloaded whole on its first read.
|
||
- Migration V28's new column is nullable rather than converted from the existing seconds. A
|
||
baseline row written before this release has no sub-second time to recover, and inventing
|
||
zero for it would make every already-synced file compare as changed and re-upload an entire
|
||
mirror on first start; `NULL` keeps those rows comparing exactly as they did, and each gains
|
||
a real value the next time its path syncs.
|
||
- Three audit entries closed without a code change, having been fixed already or being correct
|
||
as they stand: sync-folder removal is serialized against reconciliation (B41), the drain's
|
||
database contention was removed by the read-only connection pool and the claim column (B30),
|
||
and the refusals for symlinks, hard links, device nodes and `chmod` are deliberate — Drive
|
||
cannot represent them, and a local-only representation no other Proton client can read would
|
||
be worse than a clean refusal (B31).
|
||
- Six of this release's fixes were then driven live against the installed 1.8.1 on a real
|
||
account: the same-second edit uploads (B25), three rapid rewrites of a 64 MiB file publish the
|
||
third one byte for byte (B32), a write racing a download survives as a conflict copy (B39), an
|
||
upload torn by a concurrent write heals itself on the following pass (B40), a `getdents64`
|
||
walk with a 160-byte buffer against a directory mutated between pages returns every entry
|
||
exactly once (B43), and a restart of a daemon with six mounts and a busy drain stops in 110 ms
|
||
and comes back at the same thread count (B44). B42 could not be driven from a single machine —
|
||
see B86 below.
|
||
- One new bug came out of the verification: an on-demand mount serves a folder listing that
|
||
predates the sync engine's own uploads, so files the mirror pass created are invisible until
|
||
the folder is switched back (`docs/BUGS.md` B86). Nothing is lost — they are on the server and
|
||
come back on the next mirror pass — but they cannot be seen or named while the folder is
|
||
on-demand, which is also what blocks B42's live verification.
|
||
|
||
## [1.8.0] — 2026-08-16
|
||
|
||
A queued write whose node had left the local tree was retried every five seconds forever and
|
||
counted as an ordinary pending upload — 28 of them, holding 18.4 GiB, had been doing that for
|
||
30 days on the machine that found it (`docs/BUGS.md` B83). Schema: 27 — migration V27 adds
|
||
`pending_op.access_deferred_since`.
|
||
|
||
### Fixed
|
||
- The drain no longer reads "I have never heard of this node" as "you may not write this
|
||
node". A missing authority is now resolved against the remote: the node is re-interned if
|
||
it is still there, and reported as a real failure if it is gone. Ops that had been stuck
|
||
invisibly for weeks now say what is wrong with them on the first pass.
|
||
- A create or mkdir resolves its *parent* as the authority, so the refetch asks about the
|
||
folder rather than the node it has not made yet.
|
||
|
||
### Added
|
||
- Access deferrals are bounded. A deferral still costs no attempt and records no error for
|
||
the first five minutes — the case it exists for is a share downgraded mid-queue and undone
|
||
a moment later — but past that window each recheck records a failure, so the op enters the
|
||
ordinary backoff and appears in `failing_ops` and the `pdfs status` error text like any
|
||
other stuck operation. The first deferral of a run is logged at `warn` rather than `debug`.
|
||
|
||
### Notes
|
||
- No staged data is touched by any of this. A reported failure keeps its `pending_op` row and
|
||
its staged blob exactly as before; the change is in what the user is told, and in the queue
|
||
no longer waiting on a permission change that was never coming.
|
||
|
||
## [1.7.1] — 2026-08-16
|
||
|
||
Write-back throughput follow-up to 1.7.0, same audit
|
||
(`docs/plans/audit-2026-08-15-perf-locks.md`). Schema: 26 — migration V26 adds
|
||
`pending_op.claimed_at`.
|
||
|
||
### Added
|
||
- Parallel write-back drain: three workers share the queue through a `pending_op.claimed_at`
|
||
claim column (migration V26), so a large upload no longer holds every queued rename, trash
|
||
and small write behind it. Ordering is preserved per node — the claim query never offers
|
||
two workers ops for the same uid — and claims left by a crashed run are cleared at open.
|
||
- An upload already on the wire is cancelled when a newer write, a trash or a discard
|
||
supersedes it, instead of spending the uplink on a revision the next operation replaces.
|
||
|
||
### Changed
|
||
- The revision debounce is adaptive: it widens toward how long that node's last upload
|
||
actually took (bounded to 2–60 s), so a file saved faster than it can be sent supersedes
|
||
in the queue rather than mid-upload.
|
||
- Workspace deps bumped to `proton-sdk` / `proton-drive-rs` 0.6.0.
|
||
|
||
## [1.7.0] — 2026-08-16
|
||
|
||
Performance and robustness pass over the mount, from the architecture audit in
|
||
`docs/plans/audit-2026-08-15-perf-locks.md`. Schema: 25.
|
||
|
||
### Added
|
||
- Sequential-read prefetch (`Core::prefetch_ahead`): per-file sequential detection, depth
|
||
doubling 2→8, reset to 0 after a seek, skipping blocks already cached, and bounded by its
|
||
own permit budget so it can never queue ahead of a demand read.
|
||
- Single-flight block fetching (`BlockFlight`) keyed on `(uid, mtime, size, block)`, so a
|
||
demand read, kernel read-ahead and prefetch of the same block share one download+decrypt.
|
||
- In-memory block LRU (`BlockRing`) in front of *both* block paths; a block read off disk is
|
||
promoted into it, so the following ~31 kernel reads of that 4 MiB block cost nothing.
|
||
- Read-only SQLite connection pool (`ReadPool` / `Db::read`) with every `SELECT`-only method
|
||
routed at it, so a read no longer waits on the write connection.
|
||
- `nodes.path` persisted and backfilled, replacing the recursive `path_of` walk.
|
||
- Single-writer `flock` on the database; a `SQLITE_CORRUPT`/`NOTADB` file is quarantined as
|
||
`cache.db.corrupt-<ts>` (WAL sidecars too) and rebuilt rather than failing the mount.
|
||
- `VACUUM` and the deep `integrity_check` now ack immediately and run as background jobs;
|
||
`pdfs doctor` polls for the verdict.
|
||
- Staging reconcile at mount: staged blobs no queued operation refers to are re-queued, or
|
||
named in the log when they cannot be addressed.
|
||
- Queue/staging visibility — `Status` gained `parked_uploads`, `failing_ops`,
|
||
`failing_error`, `staged_bytes`, `staged_oldest_secs`; `pdfs status` prints them when
|
||
non-zero.
|
||
- `statfs` implemented against the account quota; FUSE `init` negotiates `max_readahead`,
|
||
`max_background`, `PARALLEL_DIROPS` and `CACHE_SYMLINKS`; `blksize` raised to 1 MiB.
|
||
|
||
### Changed
|
||
- `State` no longer writes to SQLite under the inode lock — mutations queue into an outbox
|
||
applied after the lock is released, so interning a 5000-child listing no longer commits a
|
||
transaction with the whole mount frozen.
|
||
- Re-listing an unchanged folder no longer touches its subtree; when a subtree really moved,
|
||
one recursive walk carries both the new path and the inherited trashed flag.
|
||
- `release(2)` no longer gap-fills from the network: it stages an incomplete blob from local
|
||
caches only and the drain thread finishes it off the dispatch loop.
|
||
- A partial write over an undrained one is merged instead of refused.
|
||
- Size-upgrade waiters park on a queue instead of occupying a worker thread, capped at 8
|
||
folders in flight; past the cap `stat` answers provisionally.
|
||
- Block-cache disk writes moved off the read's critical path; no more fsync per cached block.
|
||
- Cache LRU touches buffered in memory and flushed in one transaction.
|
||
- Search: sync-folder list and pin lookups hoisted out of the per-hit loop.
|
||
- SQLite tuned (`cache_size`, `mmap_size`, `temp_store=MEMORY`, `wal_autocheckpoint`) and
|
||
three new indexes added.
|
||
- Mount registry lock released before FUSE teardown/join; kernel notifications batched under
|
||
the state lock and flushed after it.
|
||
|
||
### Fixed
|
||
- **Data loss:** `ENOSPC` is now propagated from `write`/`fsync`/staging instead of being
|
||
swallowed, with emergency eviction wired up and scratch marked durable before any staging
|
||
error; a failed `record_pending_write` fails `release` rather than dropping the write.
|
||
- **Data loss:** the conflict sweep no longer discards queued operations after the trash
|
||
round trip.
|
||
- **Data loss:** `rescue_scratch` moves a blob with an unreadable sidecar to `recovery/`
|
||
unaccompanied instead of fabricating a sidecar for it.
|
||
- Own-sealed revisions are persisted (7-day TTL), so a restart no longer reopens the
|
||
transient-download fork window fixed in 1.1.0.
|
||
- Depth-capped `is_pinned` (cycle-safe), daemon write timeout, and every client-supplied
|
||
`limit` clamped.
|
||
- `recover_fsynced_writes` no longer scans the recovery directory on every idle pass.
|
||
|
||
## [1.6.0] — 2026-08-12
|
||
|
||
Search-index correctness and ranking (`docs/plans/search-index.md`). Schema: 21 —
|
||
migration V21 rebuilds `nodes_fts`.
|
||
|
||
### Fixed
|
||
- **B80** — 29% of the account was missing from the search index. `node_is_indexable_tx`
|
||
required a null-parent ancestor, which only the My Files root has, so every node under a
|
||
device folder was dropped from `nodes_fts` on its first upsert after the v16 backfill
|
||
(2,705 of 9,252 nodes on the account this was diagnosed against). The rule now matches the
|
||
backfill, migration V21 replays it, nodes under a trashed folder stay out, and the walk is
|
||
depth-capped.
|
||
- **B81** — the prompt labelled every Drive hit "My files", including device folders; pin
|
||
rows also took `is_dir` from `Pin::recursive`.
|
||
- **B82** — a `parent_uid` cycle hung the daemon on any short search: the recursive CTEs in
|
||
`path_of` and the pin walk use `UNION ALL`, which does not deduplicate. `path_of` is now
|
||
depth-capped at 256 and returns the truncated path.
|
||
|
||
### Changed
|
||
- Local search ranking: whole-query prefix matches get their own lane (newest first) ahead
|
||
of trigram rank, which on a home directory full of dependency trees was burying the
|
||
obvious answer — 73% of the 500 best-ranked candidates for "test" came from one Go module
|
||
cache. The single-character lane, which recovers a typo that destroys every trigram, stays
|
||
behind it.
|
||
|
||
## [1.5.0] — 2026-08-11
|
||
|
||
Schema: 20.
|
||
|
||
### Added
|
||
- **`pdfs-prompt --dmenu`** — the same search through an external dmenu-style launcher
|
||
(fuzzel, rofi, wofi, tofi, bemenu, dmenu) instead of the built-in GTK HUD, so the prompt
|
||
matches a tiling-WM desktop. A launcher filters a fixed list rather than re-querying per
|
||
keystroke, so searching is a two-step loop; `--query` skips the first step, fuzzel and rofi
|
||
get file-type icons. Configurable as `"prompt": { "mode": "dmenu", "menu": [...] }`, with
|
||
`--gtk` overriding for one invocation.
|
||
- **Configurable open-with rules** (`pdfs-core/src/opener.rs`): an ordered list of name/class
|
||
patterns in `config.json` under `open_with`, each mapped to argv and optionally wrapped in
|
||
a terminal, first match wins, falling back to `xdg-open`. Lets "open a text file" mean
|
||
`nvim` in a terminal without breaking every graphical caller. Absent config reproduces the
|
||
old behaviour exactly.
|
||
|
||
### Fixed
|
||
- The prompt and dmenu openers hand applications the cached file while matching rules
|
||
against the *original* Drive filename, so e.g. `.md` files stop being routed by their
|
||
cache-path extension.
|
||
|
||
## [1.4.0] — 2026-08-10
|
||
|
||
Schema: 20.
|
||
|
||
### Added
|
||
- **File version history** (`pdfs-fuse/src/revisions.rs` + a GTK versions dialog): list a
|
||
file's revisions, restore one, delete one, or write an old one out to a local path. A
|
||
restore re-points the file server-side, so no content crosses the wire and nothing enters
|
||
the drain queue; the daemon drops its cached content and repopulates on the next read.
|
||
- **Persistent SDK entity cache** (`pdfs-core/src/sdkcache.rs`): the SDK's decrypted node
|
||
metadata now survives a daemon restart instead of re-walking the tree. It lives in its own
|
||
`sdk_cache.db`, deliberately off the daemon's hot `cache.db` mutex, holds no key material,
|
||
needs no migrations, and is cleared on logout.
|
||
- Per-node outcomes for batch operations (`pdfs-core/src/batch.rs`): `trash`/`restore`/
|
||
`delete`/`move` report one result per node, so a node the server rejects no longer fails
|
||
the whole batch.
|
||
- Photo viewer page in the GUI and richer photo metadata in the details pane.
|
||
|
||
### Changed
|
||
- SDK telemetry routed into `tracing`, so its transfer/block-storage/request spans land in
|
||
the daemon log instead of a no-op sink.
|
||
- A file that fits in one block uploads as a single atomic request rather than the
|
||
draft/block/commit sequence.
|
||
|
||
## [1.3.0] — 2026-07-31
|
||
|
||
Schema: 19.
|
||
|
||
### Added
|
||
- **Photo albums** — `PhotoAlbums` / `AlbumPhotos` control requests, daemon-side `albums.rs`
|
||
and a GUI albums page (the former gallery view, renamed). Albums other people share with
|
||
you are included; their photos are persisted per album because a shared album's photos
|
||
never appear in your own timeline. Served stale-while-refreshing like the timeline.
|
||
- **GUI "Locations" page** — one row per mount spec: the primary `~/ProtonDrive` mount
|
||
alongside every folder this computer backs up, mirrored or on-demand. Called *Locations*
|
||
rather than *Mounts* because a mirror folder is a plain directory with no FUSE session.
|
||
It absorbs the old Computers page's actions and the mountpoint chooser from Settings.
|
||
- `role` on shared-node metadata (`viewer`/`editor`/`admin`), defaulted for wire-compat with
|
||
older clients and daemons.
|
||
- FUSE acceptance coverage for the permission-based mounts.
|
||
|
||
## [1.2.1] — 2026-07-31
|
||
|
||
Schema: 18.
|
||
|
||
### Fixed
|
||
- Migration bug that could leave the `ProtonDrive` mount unwritable after upgrade.
|
||
- Permission error on on-demand mounts.
|
||
- B78.
|
||
|
||
### Changed
|
||
- Release pipeline: `.deb` signing added and `.rpm` signing fixed.
|
||
|
||
## [1.2.0] — 2026-07-29
|
||
|
||
Sharing and multi-mount release. Schema: 18.
|
||
|
||
### Added
|
||
- **Shared with me**: shares others sent you are listed, verified and mountable, including on
|
||
the primary mount.
|
||
They appear under a synthetic `Shared with me` directory on a virtual volume
|
||
(`pdfs-fuse/src/virtual.rs`), with name collisions and over-long components resolved by
|
||
its own naming rules.
|
||
- Per-share access model (`db/share_access.rs`): the effective role of each shared root is
|
||
persisted and kept resident in mount state, so interning an inode never costs a query. The
|
||
database stays the restart/offline authority — every role change is written through before
|
||
the cache updates. A share you cannot write is presented POSIX read-only.
|
||
- Unified location model (`pdfs-core/src/mounts.rs`, `db/mounts.rs`): one presentation shape
|
||
for every local Proton Drive location, joining device sync state with a new `mount` table.
|
||
`MountMode` is `Mirror` (full local copy, reconciled both ways) or `OnDemand` (FUSE session
|
||
fetching on access), with an `Unknown` fallback so a mode written by a newer client does
|
||
not break an older one.
|
||
|
||
## [1.1.1] — 2026-07-27
|
||
|
||
Schema: 16.
|
||
|
||
### Fixed
|
||
- Trash refresh did not pull updates.
|
||
- Packaging: `PKGBUILD` was left at 1.1.0 by the 1.1.1 version bump, failing the release
|
||
workflow's tag/manifest/PKGBUILD agreement check.
|
||
|
||
## [1.1.0] — 2026-07-26
|
||
|
||
Conflict-handling and concurrency release. Schema: 16.
|
||
|
||
### Added
|
||
- Conflict sweep (`pdfs-fuse/src/sweep.rs`, 5-minute cadence): removes conflict copies proven
|
||
identical by size + `content_sha1`, surfaces divergent ones once as an activity entry, and
|
||
never removes what it cannot prove.
|
||
- Sweep mode and `.pdfsignore`-driven conflict handling; ignore rules are rebuilt per sync
|
||
pass so edits take effect without a restart.
|
||
- Substantially extended FUSE acceptance suite.
|
||
|
||
### Fixed
|
||
- **B69** — spurious `(sync-conflict)` copies. Revision identity now uses the server's
|
||
`revision_id` rather than `(mtime, size)`, which the server re-stamps on seal; the planner
|
||
gained `AdoptBaseline` so an equal-size no-baseline pair is recorded instead of forked on
|
||
first reconcile.
|
||
- **B70** — browser downloads into an on-demand mount had their in-flight temporaries sealed
|
||
as canonical revisions. Transient names are recognised and held until the name is finalized.
|
||
- Assorted race conditions and concurrency bugs on the write/drain paths.
|
||
|
||
### Changed
|
||
- Workspace deps bumped to `proton-sdk` / `proton-drive-rs` 0.2.2.
|
||
|
||
## [1.0.0] — 2026-07-22
|
||
|
||
First stable release: FUSE files-on-demand mount, sync daemon under `proton-drive.service`,
|
||
`pdfs` CLI and the GTK4 app, on top of the pure-Rust Proton Drive SDK. Schema: 16.
|
||
|
||
### Fixed
|
||
- The outstanding FUSE defects tracked in `docs/BUGS.md`, plus a truncate defect, validated
|
||
by a new POSIX compliance suite for the filesystem.
|
||
|
||
[3.5.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.5.0
|
||
[3.4.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.4.1
|
||
[3.4.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.4.0
|
||
[3.3.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.3.0
|
||
[3.2.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.2.1
|
||
|
||
[3.2.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.2.0
|
||
|
||
[3.1.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.1.2
|
||
|
||
[3.1.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.1.1
|
||
|
||
[3.1.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.1.0
|
||
|
||
[3.0.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v3.0.0
|
||
|
||
[2.10.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.10.0
|
||
[2.9.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.9.0
|
||
[2.8.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.8.2
|
||
[2.8.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.8.1
|
||
[2.8.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.8.0
|
||
[2.7.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.7.0
|
||
|
||
[2.6.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.6.2
|
||
[2.6.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.6.1
|
||
[2.6.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.6.0
|
||
[2.5.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.5.0
|
||
[2.4.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.4.0
|
||
[2.3.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.3.0
|
||
[2.2.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.2.2
|
||
[2.2.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.2.1
|
||
[2.1.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.1.0
|
||
[2.0.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v2.0.0
|
||
[1.11.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.11.1
|
||
[1.11.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.11.0
|
||
[1.10.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.10.2
|
||
[1.10.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.10.1
|
||
[1.10.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.10.0
|
||
[1.9.3]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.9.3
|
||
[1.9.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.9.2
|
||
[1.9.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.9.1
|
||
[1.9.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.9.0
|
||
[1.8.2]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.8.2
|
||
[1.8.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.8.1
|
||
[1.8.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.8.0
|
||
[1.7.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.7.1
|
||
[1.7.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.7.0
|
||
[1.6.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.6.0
|
||
[1.5.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.5.0
|
||
[1.4.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.4.0
|
||
[1.3.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.3.0
|
||
[1.2.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.2.1
|
||
[1.2.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.2.0
|
||
[1.1.1]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.1.1
|
||
[1.1.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.1.0
|
||
[1.0.0]: https://github.com/narrrl/proton-drive-linux/releases/tag/v1.0.0
|