A folder pinned before its mkdir landed kept the placeholder uid in pins.uid, which named no node once the landing moved the row to the real uid. The landing now moves the pin with it.
99 KiB
Changelog
All notable changes to the Linux client since the 1.0.0 release. Format follows
Keep a Changelog; 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]
Schema: 38. 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.
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 issueslists the changes that need attention;pdfs sync queueshows the issue next to each change.
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": falseinconfig.jsonbrings back the old way for this release.
Fixed
- 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 where another one is leaving no longer lands as a conflict copy. Its upload waits for the rename or trash that frees the name (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 -- --ignoredruns 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=1mounts 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
pdfsoutput into a closed pipe stays quiet. The console report has one line per case and a summary table, and--journal-checkalso 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 renameandpdfs mkdirtake paths in on-demand folders. They answered "is not under the mountpoint" for a pathpdfs movetook.rmandrenamerefuse a path that names a mount's root (B88).pdfs ls | headno longer panics with "Broken pipe". A closed stdout exits 0; any other write error exits 1.- A file removed, renamed or moved with
pdfsstops resolving under its old name at once, instead of for up to 30 seconds (B114). pdfs lsand 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 movemoved keeps its path inpdfs sync queueand is no longer put off once after the resume (B117). - Creating a file or folder through the mount no longer fails with
ENOENTwhen 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 statuscounts 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 errorthrough the mount and with "out of date" frompdfs move, and a rename across folders took up to 10 seconds (B111, B121). - A rename across folders no longer fails with
Input/output errorwhen 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-rs0.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.servicewas wanted bydefault.target, which is reached before the desktop handsDISPLAY/WAYLAND_DISPLAYto the user's systemd. The first keyring call then started the Secret Service without a display; on KDEksecretdcrashed at every login and the first start of the service failed. The unit is now wanted bygraphical-session.target. Existing installs switch over on the next sign-in or "Start on login" toggle, or withsystemctl --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 --jsonprints the trash listing as JSON.- The FUSE acceptance suite tests the signed-in account by default.
scripts/fuse-acceptance.shwith 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-onlyruns 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-rs0.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 errorand 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 rmnow 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.replacedo) made reads through handles already open on the old file fail withInput/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
erroruntil 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 errorinstead 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 moveand 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.mvbetween 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 movetakes several sources.pdfs move a b c DESTmoves 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. 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'sexec-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-promptprocess 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.FileManager1and 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.jsonin 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_withapply 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.shpublishes 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 thepdfsCLI. 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=1to 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 (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.cssimports 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.csscan 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
pdfsCLI and messages from the daemon stay in English. docs/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 inlocale/. Building the source packages now needsgettext.
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 resumehold 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 queueandpdfs 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 conflictsandpdfs conflicts resolve <path> --keep original|copy|bothdo 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, andpdfs sync listmarks 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 0does the same, andpdfs sync limiton its own prints the current caps. The caps are kept inconfig.jsonacross 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_hiddeninconfig.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-appaccepts--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 andpdfs-queryshow 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-albumandremove-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,
+/-,1for 100 % and double-click, and pan by dragging;0fits 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 favoritecommand.
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
.tmpuploads normally..tmpand.tempwere treated as writer temp-file suffixes, so ordinary files — a Google Photos Takeoutindex.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 diagnosticsalso 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 photosblocks 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.jpgandPXL_….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 newPhotosRefreshStatusrequest 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-Afteris 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
$HOMEand 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 stoptimed 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
lruIterMut Stacked Borrows advisory. The SDK now depends onlru0.18.attyunaligned read advisory. The tray moved toksni0.3, which no longer pullsdbus-codegen→clap2 →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
gtk40.11,libadwaita0.9,webkit60.6,rusqlite0.40 (bundled SQLite),fuser0.18,directories6,sha1/sha20.11,base640.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-meshow 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.DirEntrygainsshared_by,shared_atandshared_by_unverified(defaulted, so older front-ends and daemons interoperate).
Changed
- A queued move-and-rename lands as one request.
drain_renameused 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 callsmove-multiplewith 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
TrashTimeas well as the legacyTrashedfield, 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_sessionslept a flat 3 s between checks, and a check is not just a sleep —auth::loadbuilds a freshkeyring::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 atinfoinstead 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 --nowis a no-op on an already-active unit, so the GUI login and the tray's Connect left it sleeping.service::enable_startnow runsenable+restart, which interrupts the sleep and still starts a unit that is down, andpdfs loginrestarts the unit when it is already active — deliberately without touchingenable, so a headless setup drivingpdfs daemonby 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-runreports without touching anything,--from/--toaim the run at the days an import landed on, and--limitcaps it. Followed withpdfs redate-status, stopped withpdfs 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--dmenuhas to make searching two steps — type, Enter, then results.fzfcan 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--disabledon 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 —
--fzfspawns a terminal for itself (foot, ghostty, kitty, alacritty, wezterm or xterm, whichever is installed), each with the app-id/classpdfs-promptso 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-metadatait 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.jpgandScreenshot from 2023-01-01 10-29-50.pngall 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_withwas 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@dirrule likealacritty --working-directory {}opened nothing at all. Opened children now get their own process group and no inherited stdio. Affects every front end; only--fzfcould 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-rs0.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.rsinto a sharedpdfs-gui/src/query.rs, so--dmenuand--fzfcannot 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 viaexiftool. 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 —
exiftoolmissing, 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
exiftoolin 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 missingraw; both classification helpers now read from one shared table instead of two hand-maintainedmatches!arms. - The tray and desktop-entry icons were wrong/missing;
.desktopfiles 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-runto see what it would do,--waitto 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 andArchive/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 transfersand the GUI's activity view.pdfs import-statusshows the counts,pdfs cancel-importstops 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-rs0.6.1): album writes —ProtonPhotosClient::create_albumandadd_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"andCargo.lockre-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
SetCacheBudgetround-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.mdB86). pdfs lsandpdfs refreshaccept 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.mdB85).
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_atdoes 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 000subtrees for permission failures, a real/dev/shm↔/tmpdevice boundary for the cross-filesystem fallbacks, SQLite'smax_page_countfor 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 thepreserve_write_atthat already existed, andwalk_local_treelifted 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
lsandrefreshto 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.mdB88). - 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.mdstill 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 pinuses, 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 failEIOwith 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.mdB42). - 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.mdB25). - 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.mdB39). - 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.mdB40). - Listing a directory that changes while it is being read no longer skips or repeats entries.
readdirresumed 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.mdB43). - 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.mdB32). - 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.mdB44).
Notes
- The size
statreports 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.mdB85. 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;
NULLkeeps 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
chmodare 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
getdents64walk 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.mdB86). 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_opsand thepdfs statuserror text like any other stuck operation. The first deferral of a run is logged atwarnrather thandebug.
Notes
- No staged data is touched by any of this. A reported failure keeps its
pending_oprow 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_atclaim 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-rs0.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 everySELECT-only method routed at it, so a read no longer waits on the write connection. nodes.pathpersisted and backfilled, replacing the recursivepath_ofwalk.- Single-writer
flockon the database; aSQLITE_CORRUPT/NOTADBfile is quarantined ascache.db.corrupt-<ts>(WAL sidecars too) and rebuilt rather than failing the mount. VACUUMand the deepintegrity_checknow ack immediately and run as background jobs;pdfs doctorpolls 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 —
Statusgainedparked_uploads,failing_ops,failing_error,staged_bytes,staged_oldest_secs;pdfs statusprints them when non-zero. statfsimplemented against the account quota; FUSEinitnegotiatesmax_readahead,max_background,PARALLEL_DIROPSandCACHE_SYMLINKS;blksizeraised to 1 MiB.
Changed
Stateno 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
statanswers 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:
ENOSPCis now propagated fromwrite/fsync/staging instead of being swallowed, with emergency eviction wired up and scratch marked durable before any staging error; a failedrecord_pending_writefailsreleaserather than dropping the write. - Data loss: the conflict sweep no longer discards queued operations after the trash round trip.
- Data loss:
rescue_scratchmoves a blob with an unreadable sidecar torecovery/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-suppliedlimitclamped. recover_fsynced_writesno 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_txrequired a null-parent ancestor, which only the My Files root has, so every node under a device folder was dropped fromnodes_ftson 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_dirfromPin::recursive. - B82 — a
parent_uidcycle hung the daemon on any short search: the recursive CTEs inpath_ofand the pin walk useUNION ALL, which does not deduplicate.path_ofis 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;--queryskips the first step, fuzzel and rofi get file-type icons. Configurable as"prompt": { "mode": "dmenu", "menu": [...] }, with--gtkoverriding for one invocation.- Configurable open-with rules (
pdfs-core/src/opener.rs): an ordered list of name/class patterns inconfig.jsonunderopen_with, each mapped to argv and optionally wrapped in a terminal, first match wins, falling back toxdg-open. Lets "open a text file" meannvimin 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.
.mdfiles 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 ownsdk_cache.db, deliberately off the daemon's hotcache.dbmutex, 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/movereport 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/AlbumPhotoscontrol requests, daemon-sidealbums.rsand 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
~/ProtonDrivemount 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. roleon 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
ProtonDrivemount unwritable after upgrade. - Permission error on on-demand mounts.
- B78.
Changed
- Release pipeline:
.debsigning added and.rpmsigning 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 medirectory 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 newmounttable.MountModeisMirror(full local copy, reconciled both ways) orOnDemand(FUSE session fetching on access), with anUnknownfallback 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:
PKGBUILDwas 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'srevision_idrather than(mtime, size), which the server re-stamps on seal; the planner gainedAdoptBaselineso 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-rs0.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.