Skip to content
proton-cli

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Adding a version section here is what publishes a release, so this file is the one place a version is decided: see Releases. Versions that shipped before this file existed are on the releases page.

  • Building from source needs Go 1.27 or newer.
  • macOS 12 Monterey. Releases run on macOS 13 Ventura or later; on macOS 12, stay on 5.0.0.
  • Files uploaded to Drive, attached to mail or added to Pass items were typed from the system’s own list of file types, so on a system without one most were stored as application/octet-stream and the Drive index skipped their text. They get the type Proton’s web client gives them, on every system.
  • Releases are built with Go 1.27, which carries Go’s August fixes to TLS, HTTP and URL handling that 5.0.0 was built without.
  • Sign in without typing a password - account login --qr prints a code that a device already signed in approves, and account sessions create approves one from here, so a machine reached over SSH never sees your password.
  • The rest of Proton’s security pages - account keys manages the keys of your addresses, account settings emergency-access and recovery-contacts choose who can help you back in, account breaches is Dark Web Monitoring, and account delete closes the account.
  • An inbox with less noise - mail mailing-lists shows who writes to you as a list and files or leaves them, mail messages trackers list names what a message tried to track, and links read without their tracking once image-proxy is on.
  • Let people book a time with you - calendar booking-pages hands out a link for booking an appointment in your calendar, and calendar settings links publishes a calendar as a feed anyone can follow.
  • account login --qr signs in by approving a code on a device that already holds a session, with nothing typed here, and account sessions create CODE is the approving side.
  • account keys lists, shows, exports, creates, imports and deletes the keys of your account and addresses, and keys update moves the primary or marks a key obsolete or compromised. Every change asks for your password, even signed in.
  • account settings emergency-access and account settings recovery-contacts choose who can help you back into your account, and list --incoming shows who chose you. Emergency access needs a paid plan and opens after --wait, 7 days unless set; emergency-access access --as PROFILE then saves the account that trusted you as a profile.
  • account breaches is Dark Web Monitoring: the breaches your addresses were found in, what each exposed, resolve and reopen, and enable and disable, with --emails for the emails it sends.
  • account settings sentinel shows and switches Proton Sentinel, and account settings notifications switches each email and notice Proton sends you by name.
  • account delete deletes the account and everything in it, and then removes the profile from this machine.
  • mail mailing-lists lists the senders that write to you as a list, files their mail with update --into and --mark-read, and unsubscribes from them.
  • mail messages trackers list names each tracker a message’s images carry and each link that had its tracking taken out. It needs image-proxy on.
  • mail messages count and mail conversations count show every folder, label and category tab with its total and unread count, or one with --folder.
  • --request-receipt on mail messages send, reply, forward and mail drafts asks for a read receipt, messages get shows a Receipt line, and mail messages receipt sends one back.
  • mail settings imports brings another mailbox in over IMAP, and follows, cancels, resumes, undoes or deletes an import, ones started in Proton’s apps included.
  • mail settings categories shows or hides each category tab and turns its notifications on and off, and --folder primary lists the Primary tab.
  • mail settings folders reorder and labels reorder set the order Proton’s apps show, naming the first ones or --alphabetical, and both listings come back in that order.
  • mail settings set takes image-proxy (Block email tracking), category-view, category-view-counters, font-face, font-size, block-sender-confirmation and daily-notifications.
  • Mail listings and bulk verbs take --has-attachments, --read, and --via ADDRESS for mail that arrived at or left from one of your addresses, which --to misses for Bcc, lists and catch-alls. Listings take --sort size.
  • mail conversations update --expires DURATION|never makes whole threads delete themselves, or stops it, and listings mark mail that will with ⧗ in FLAGS and expires in JSON.
  • calendar booking-pages makes a link anyone can open to book a time with you in the calendar you pick, Monday to Friday 09:00-17:00 unless --available says otherwise. Needs a paid plan.
  • calendar settings links publishes a calendar as an .ics feed anyone can follow, showing only busy times unless --access full, and lists, renames and revokes what has been handed out.
  • calendar settings holidays list shows the public holidays calendars Proton offers, and calendars create --holidays Austria adds one.
  • calendar settings calendars leave gives up a calendar somebody shared with you, and calendars update --default makes a calendar the default.
  • calendar busy-times list EMAIL... shows when people are busy, over the next 7 days unless --after or --before say otherwise. Needs a Duo, Family, Visionary or business plan.
  • contacts create and update take --photo: an image file, - or a web address.
  • contacts emails list and update set how mail to each address goes - --email-format, --encrypt, --sign and --scheme - and proton sends it that way.
  • A contact keeps every note, organization, job title, role, nickname, language and time zone it holds: each flag repeats, and update has a --clear- flag for each.
  • drive photos albums share and drive photos share share an album or a single photo with people, and drive photos links makes a public link for a photo. An album takes no public link.
  • drive items abuse, drive items revisions abuse and drive invitations abuse report to Proton something shared with you. --good-faith is required, and a report cannot be withdrawn.
  • pass vaults hide and unhide keep a vault out of your own listings, as the Pass app does.
  • pass items exclude and include take a login out of Pass Monitor’s checks and put it back, and items list --excluded lists those.
  • pass items get shows when a Pass app last filled an item in, and items list --sort used orders by it.
  • pass items update --simplelogin-note and --clear-simplelogin-note change an alias’s SimpleLogin note, --clear-display-name removes its display name, and pass aliases create --note gives a new alias a note.
  • pass breaches resolve marks an address’s breaches resolved, and enable and disable --type proton|alias switch watching for every address of that kind on a paid plan; list --type filters by it.
  • Breaking. contacts keys pin and unpin are contacts keys trust and untrust, and report trusted and untrusted. Their --scheme and --no-encrypt are contacts emails update --scheme and --encrypt off, and contacts keys list drops ENCRYPT and SCHEME.
  • Breaking. Contact JSON carries nicknames, organizations, job_titles, roles, notes, languages and timezones lists in place of nickname, org, title, role, note, language and timezone.
  • Breaking. Mail sent outside Proton follows the account’s sign and pgp-scheme settings where a contact states none, so with sign on or pgp-scheme pgp-inline it goes out signed that way.
  • Breaking. calendar events create and events import without --calendar go into your default calendar rather than the first one listed.
  • Breaking. calendar settings calendars list has a KIND column, a calendar shared with you is shared rather than personal, and each command refuses what a calendar’s kind does not take - events into one you cannot write to, sharing one that is not yours.
  • Breaking. --folder with a category tab selects only mail still in the inbox, as the tab shows it. mail messages empty refuses a tab, inbox, drafts, sent, starred, archive, all and scheduled, and move --into refuses starred, drafts, sent, all, scheduled and snoozed.
  • Breaking. With almost-all-mail on, --folder all and a bulk verb given no --folder leave spam and trash out.
  • Breaking. settings set --output json reports value as the word set took ("30d") rather than Proton’s number, in every app. remove-image-metadata takes on or off, and spam-action takes ask.
  • Breaking. With image-proxy on, mail messages get and conversations get print links without their tracking, in text, html and JSON, and replies and forwards quote them that way. --render raw is the body as sent.
  • Breaking. drive items share get --output json names the item under ref rather than path.
  • Breaking. pass breaches get --output json reports each breach’s state (new, open or resolved) instead of resolved.
  • Breaking. account get shows only the display name under Name, and the username on a row of its own.
  • Breaking. Pass listings across vaults and pass trash empty leave out vaults hidden in the Pass app. --vault still reaches one, and pass export still backs it up.
  • Breaking. pass items update --display-name "" is refused; use --clear-display-name.
  • mail settings get shows every key set takes, under the name set knows it by.
  • mail messages watch covers starred mail, and with categories on, only the tabs that notify.
  • mail messages update --expires keeps an expiry the sender set and sets none in trash or spam, and says what it left alone.
  • Bulk mail verbs name each message or thread Proton refused, and count only what landed.
  • A mail index built before this release is read again once: a filtered search says it has fallen behind until index update runs, which keeps the bodies.
  • drive photos upload leaves a photo the library already holds alone and prints Nothing to upload with its ID; JSON carries duplicate_of.
  • drive invitations list names what an invitation offers and what kind of thing it is.
  • A command that takes an address refuses anything but a bare address before signing in.
  • Flags that contradict each other are refused as --a and --b contradict each other. wherever they meet.
  • --folder completes your own folders and labels, and --into completes folders in mail and vaults in Pass.
  • The published openapi.yaml documents the image proxy, lists the query parameters of every request, and gives optional fields their real type rather than string. Regenerate anything built from it.
  • calendar settings set default-calendar. Use calendar settings calendars update REF --default.
  • mail settings set message-buttons, which no Proton app offers.
  • member_count from calendar JSON, which only ever counted your own memberships.
  • Changing your default address made account login --user with the old one exit 4 until the profile was signed out, and could break every command that asks for your password again. Any of the account’s addresses resumes the session.
  • account login --dry-run signed in for real. It previews and changes nothing.
  • account sessions revoke could not revoke another session from a script. It takes --password-file, which Proton asks for.
  • A mistyped address on mail settings addresses get and update, mail settings forwarding create, mail settings smtp-tokens create and pass settings domains update --catch-all acted on the account’s only address, or read as ambiguous. It is not found, exit 3.
  • mail messages unsubscribe only ever sent Proton’s one-click request. It leaves by whichever of the three ways the list offers, says which, and refuses a list that offers none.
  • mail settings folders create --parent and update --parent took only an ID, though the example named a folder. They take a name or short ID, and --parent none moves a folder to the top level.
  • After a partly refused bulk change, ids under --output json listed what was asked rather than what changed. Affects the bulk mail verbs, drive items trash and delete, drive photos delete, drive volumes restore and account keys reactivate.
  • pass breaches list showed most breached aliases as clean. Each count lands on its own row, and one that does not come back reads ? rather than 0.
  • calendar settings calendars update dropped the reminder given --remind or --remind-all-day beside --no-remind. It refuses the pair.
  • An all-day event on a day the clocks change lasted 23h or 25h rather than 1d.
  • A mistyped --attendee failed as a bug. It is refused before signing in.
  • An upload over a capitalised name another Proton app wrote landed beside it instead of being refused, and drive photos upload could recognise no photo Proton’s apps backed up.
  • A passphrase from pass generate --words could hold one of four hyphenated words, so split on hyphens it read as more words than were asked for.
  • pass vaults update finds a vault by its name, as its example shows.
  • An error naming something of yours keeps your spelling of its name.
  • A message signed by a key its owner marked compromised reads Signature: invalid, and Pass invitations and forwardings no longer trust such a key.
  • Your account’s own security settings - the password, two-password mode, an authenticator app, security keys, and recovery by email, phone or phrase are set from proton account settings, and proton account security-log reads Proton’s record of the sign-ins. Every write reads its secrets from files, so an unattended script can make the change.
  • Search inside your files - drive items list --keyword matches what your text files say, not only what they are called, once proton index create drive has read them.
  • Messages behind a password - proton mail protected opens one from the terminal, its attachments and a reply included, and needs no account to do it.
  • Your own alias domain, and access tokens - pass settings domains takes a domain you own from its DNS entries to a working catch-all, and pass settings access-tokens mints a token that reads only the vaults it is handed.
  • account settings reaches what Proton’s security page does: password set, second-password for two-password mode, two-factor for an authenticator app, and recovery-email, recovery-phone and recovery-phrase. Each proves your current password and reads every secret from a file, so an unattended run can make the change; recovery codes and a recovery phrase are shown once and kept nowhere.
  • account settings security-keys registers a security key you plug in, renames it and removes it. Registering one used to mean going to the browser.
  • account security-log reads Proton’s sign-in log, says whether it is recording and whether it keeps IP addresses, and security-log delete clears it. Nothing you do through proton appears in it: Proton records a sign-in from a named client, and this is not one.
  • pass settings domains handles an alias domain you own: add it, read every DNS entry it needs beside the verdict Proton last reached, choose the domain new aliases take, and once it is verified set its catch-all mailboxes, display name and random prefix. Needs a paid plan.
  • pass settings access-tokens mints a token that reads the vaults it is handed and nothing else, for pass-cli or an agent working through it. The token is on the screen once, and access-tokens activity says what it has done. Needs a paid plan.
  • mail settings smtp-tokens lists, shows, creates and deletes the tokens a printer or another service sends mail with. A token sends from one custom domain address, which is the SMTP username, and it is on the screen once.
  • mail protected opens a password-protected message somebody sent you: read it, download its attachments and reply, named by the link that arrived or the id in it, with the password from a file or standard input. It needs no account.
  • drive items list --keyword matches what a text file says as well as what it is called, once index create drive has read the text: up to 1 MB a file, with a PDF, an image or anything larger searchable by name alone. A drive index built before this holds no text until index delete drive and index create drive rebuild it.
  • drive items download --recursive takes a folder, which lands as a directory of its own name inside --dest-dir.
  • Breaking. A listing’s JSON key is one word, so pass breaches list --output json keys its rows under watched_addresses rather than watched addresses. Every other collection is unaffected.
  • Breaking. pass settings domains list --output json rows carry status, id, aliases and created in place of mx_verified.
  • Breaking. The published openapi.yaml takes Drive from Proton’s own Drive spec: 113 operations are back or newly there, albums and file revisions among them, and Drive’s operations are named the way Proton names them. Regenerate anything built from the spec.
  • A filtered Drive listing shows each item’s full path.
  • mail messages get carries an Expires line wherever Proton set one.
  • A password-protected link handed to mail messages get says which command opens it instead of failing to find it.
  • account keys reactivate --recovery-phrase failed with Path not found. Those endpoints answer at Proton’s account host, and the CLI was asking the host Mail is served from.
  • Proton temporarily limiting an account exited 4, which a script reads as an ambiguous reference. It exits 5, the code to wait and retry on.
  • A calendar this account cannot open stopped taking in changes after the first one, so a search over it answered as of the build. It keeps up now, each event indexed by the day and time Proton keeps in the clear.
  • index list counted a calendar’s unreadable events again every time the calendar was read, so the number grew past what the index holds. It counts what is in the index.
  • A message that arrived or was edited while its body could not be fetched was missing from every indexed listing until the mailbox was read again. It is indexed by its envelope, and the next run fetches the body.
  • A page of changes the index could not apply was skipped for good - by index watch, and on disk by index update for a calendar. It is asked for again.
  • A Drive index whose files a password reset had moved to a new volume read the whole tree on every run and never caught up. It follows the files to the new volume.
  • A damaged record in an index took every record after it along, without a word, and the index still called itself complete. What survives is kept, and the app is read again for the rest.
  • index list said 0 bodies for a mail index built before 4.2.1 until index create or index update ran. Any command that opens the index puts the count right, a search included.
  • A Drive or calendar listing answered from an index that its own catch-up had just found wanting. It reads Proton until the index is brought up to date.
  • Contact writes - contacts update, contacts merge, contacts keys pin and unpin - failed about two in five times when run back to back, as though the card had been wrong. Proton was refusing the pace; they wait it out.
  • mail messages empty and mail settings filters apply were refused while Proton was still finishing an earlier job on the mailbox. They wait for it, up to a minute.
  • mail messages empty reported emptying one message, a number it never had. It names the folder or label, and --output json carries folder or label as kind rather than message.
  • Mail is indexed in two passes: every message first, then the bodies newest first. A filtered listing such as --unread, --from or --after is answered from the index minutes into a build rather than after the hours the bodies take, and a --keyword search says how many bodies it has read while the rest arrive. index list names both counts, and --output json carries bodies.
  • index watch finishes a build that was interrupted, and reads an app again where Proton could not say what changed. It still indexes nothing that has no index.
  • Downloading from or uploading into a share link failed with Path not found. Proton retired the endpoints behind --link and --shared.
  • An index went on reporting itself complete after Proton could not describe what had changed, silently missing whatever happened in the gap. index create and index update read the app again and take out what is gone, and a search says the index has fallen behind until they do.
  • A message whose text changed, a draft being written among them, kept its old text in the index.
  • A file uploaded together with its folder was indexed at the top of Drive rather than inside it, and stayed there.
  • Items inside a trashed or deleted folder stayed in an indexed listing.
  • A calendar this account cannot open was missing from an indexed search without a word. Its events go in by their times and are counted under UNREADABLE, and a calendar that could not be read leaves the build unfinished rather than claiming to hold it.
  • A --keyword matched every message of a thread when one of them quoted the word.
  • A reference beginning with -, which about one ID in sixty-four does, was refused by the command that had just printed it.
  • index update --output json called one entry an entrie.
  • proton index keeps an encrypted copy of your mail, files and events under ~/.config/proton-cli/index/, and a search reads it once it exists: --keyword reaches message bodies, --from and --to match display names, a Drive --pattern is answered without opening every folder, and an event is found by what it says. A build can be stopped and resumed, index update and index watch keep it current, and index delete takes it off the machine. Proton’s search operators stop applying to an indexed search, which matches every term literally.
  • calendar events list and events export take any date range. Proton refuses a window wider than a few months, so a semester failed outright; the range is fetched six weeks at a time and the answers joined.
  • A calendar that cannot be read is reported under the listing or export rather than leaving it short without a word, and an export says which events it could not decrypt.
  • A starred mail listing returned a short page with a count that included the messages it had dropped. --starred narrows before paging.
  • pass trash list, pass trash restore --all and pass trash empty always came back empty, however much was in the trash.
  • Breaking. A calendar listing given one end of its range takes the other 30 days away from it, rather than always ending 30 days from today. calendar events list --after 2027-01-01 lists that January instead of nothing, and --after 2020-01-01 lists January 2020 rather than everything since. events export and reminders list read the same way.
  • Breaking. A mail listing refuses --before earlier than --after, as a calendar listing already did. It used to return an empty listing and exit 0.
  • A malformed --after or --before on a mail listing is refused before signing in, as --after expects YYYY-MM-DD. rather than as Go’s own parse error.
  • calendar events list and events export said to narrow the range with --start and --end; the flags are --after and --before. All three calendar listings now state their default range as well.
  • mail messages reply --help described --all; the flag is --everyone.
  • pass items totp on an item with no two-factor secret said to store one with --totp-uri, which no longer exists. It names --secret-file totp-uri=FILE, and so does the refusal of a secret no code can come out of.
  • The diagnostic log no longer keeps the name of a Pass item, vault, attachment or Drive file written into a failure. proton report stands <name> in its place.
  • pass breaches watches an address for leaks: the account’s own addresses and the hide-my-email aliases in your vaults are listed beside each other, and an address you own elsewhere can be added, verified, paused and removed. pass items list --risk compromised|missing-2fa|reused|weak is the password half; only compromised reaches the network, sending six characters of a password’s SHA-1 and never the password.
  • drive items share add and pass vaults|items share add take an address with no Proton account. Proton emails an invitation, share get says who is ready, and share confirm hands the key over once they have joined. update, remove and resend reach the held offers too.
  • mail settings domains handles a custom domain: add one, read every DNS entry it needs beside the verdict Proton’s last check reached, point stray mail at a catch-all address, and give the domain up. Needs a paid Mail plan.
  • pass items passkeys list and passkeys delete show what a login signs in with and take one off. A login whose only way in is a passkey no longer reads as a login with nothing in it.
  • Breaking. One name per idea across the tree: --page-size is --limit, --edit is --access viewer|editor, --move-to is --into, --plain is --html=false, --message on mail settings autoreply set is --body, --clear-catch-all is --catch-all none, and update’s --check and --reinstall are --dry-run and --force.
  • Breaking. A Pass vault’s --icon and --color take Pass’s names rather than numbers: --icon star --color teal.
  • Breaking. Calendar listings take --after and --before rather than --start and --end, both days included and read in your working zone. Mail’s --before now includes its own day too.
  • Breaking. One command per idea: mail messages expire is mail messages update --expires, drive items share link and unlink are the drive links collection, mail settings senders forget is remove, and calendar settings calendars share list is share get.
  • Breaking. contacts groups and contacts keys take an address as an argument rather than --email.
  • Every --…-stdin flag. The matching --…-file takes - for standard input.
  • pass aliases options. pass settings domains list and pass settings mailboxes list say the same.
  • A listing the CLI pages itself answers whole unless a page is asked for. pass aliases list reported fewer aliases than the account holds, which read as a short answer rather than a wrong one.
  • curl … | sh -s -- --help prints the help instead of failing with an awk error - the one flow the script’s own header documents.
  • An installed binary that cannot run on the machine fails the install and shows what the program said, instead of reporting success with the version silently missing.
  • The install script’s wget fallback gives up as quickly as its curl path rather than retrying for a quarter of an hour.
  • pass import reads exports from sixteen password managers, and --manager names which one wrote the file. pass export --format csv|json writes the two flat shapes beside the archive.
  • Files go on a Pass item with --attach and --detach, and pass items attachments lists, downloads, renames and restores them. They need a paid Pass plan, and a backup carries them both ways.
  • A message Proton distrusts carries phishing, suspicious or dmarc_failed in a listing and a read. mail messages mark legitimate and mark phishing answer a verdict.
  • account keys reactivate opens the keys a password reset locked, with the previous password, a recovery phrase or a recovery file. drive volumes list, restore and delete handle the Drive volume the same reset locked.
  • Data sealed to a key a password reset locked says so and names the remedy, instead of failing as a bug worth reporting.
  • A message that will not decrypt fails the read rather than printing (decryption failed) where the body goes. A thread drops that message and warns, an export keeps the armour, and a reply refuses rather than quoting ciphertext.
  • Every Drive command works in the active volume rather than whichever one Proton lists first.
  • Both attachment download commands take an attachment’s own name, not only its ID.
  • YAML is rendered from the JSON, so api keeps Proton’s key order and an integer too wide for int64.
  • A Pass backup restores with its dates, its trash and its alias addresses intact, and an export carries trashed items.
  • An archive too large to read says so, instead of being reported as a file some other password manager wrote.
  • Prompts are no longer padded to a common column.
  • A raw proton api request counts as a deletion whichever method it carries, so PROTON_CONFIRM='deletions=deny' covers it. Proton deletes with PUT as well as DELETE, so a fence reading the method never held. That policy now turns proton api off entirely, reads included.
  • An export written with --force is private whatever the permissions of the file it replaced. Files are published by rename, so a destination cannot be swapped for a symlink between the moment it is checked and the moment it is written.
  • Text from a message can no longer drive your terminal. On a terminal only proton’s own styling is passed through and anything else is shown; redirected and piped output carries its bytes exactly.
  • The log proton report collects no longer keeps an address written into a warning, or the name of a Pass item written into an error.
  • Release artifacts are built from actions pinned to commits rather than moveable tags, and only from a commit the checks ran on.
  • drive items list, get and download take --link URL, opening a public link somebody sent you - with no Proton account at all, if you have none. drive shared add keeps a link so --shared REF opens it later, and shared remove forgets one.
  • --computer NAME works inside a computer syncing through the Drive desktop app, and --shared REF inside an item somebody shared with you. A path still means what it always meant; the flag says which tree it is in.
  • drive items upload and items create take --link and --shared, putting a file or folder into somebody’s link. Signed in, the upload names your address; with no account it names nobody. A link that allows viewing only says so before anything is planned, and --if-exists replace is refused there because a link takes no new revisions.
  • drive items update and items delete work inside a link on what you uploaded there yourself, for an hour, signed in. A link has no trash, so a folder goes with everything in it.
  • drive shared leave gives up an item somebody shared with you directly. Only a new invitation brings it back.
  • mail settings addresses create, disable, enable and delete. A new address is given its first key; the keys of an address that has them are never touched. Proton asks for your password, so they take --password-file and --password-stdin.
  • mail settings addresses create alice@pm.me turns on the account’s short-domain address, and addresses reorder makes an address the default - name the ones that should come first, and the rest keep their order.
  • mail settings forwarding accept and decline answer a forwarding somebody sent you, and delete, disable, enable and resend handle one in either direction.
  • pass settings extra-password get, enable and disable, which the Pass app offers under Settings. Enabling stores a verifier and never the password; disabling proves the current one first.
  • Forwarding one of your addresses outside Proton costs that address its end-to-end encryption. forwarding create says so before it acts, and taking the last one down turns encryption back on.
  • Every file uploaded to Drive now carries its own encrypted record of itself - size, block sizes, modification time and SHA-1 - so uploads report a checksum and other clients show the real modification date.
  • A command that is waiting shows a sign of life on stderr after a tenth of a second, taken back by the first byte of the answer, so one that has printed nothing no longer looks hung.
  • A wait for Proton to answer, or to stop rate limiting, is a sentence with the delay in it rather than a log record.
  • A wrong number of arguments is refused by the name the usage line shows, with a working example to copy.
  • proton help <command> shows the screen --help shows, and --help after a word the command tree does not hold is refused rather than answering with the parent’s.
  • Reading - from a terminal says so and names the key that ends it, and a second Ctrl-C ends a run that did not stop on the first.
  • drive shared list says what each row lets you do, and drive items get says whether a link allows editing and names you on your own uploads into one. An item read through a link reports no author, which is all Proton serves there.
  • Sending mail and creating Drive share links from an address that has ever accepted a forwarding. Everything leaving the address was encrypted to every key it holds, and a forwarding key carries no encryption key at all, so the send failed outright and told you to file a report. Affects any account whose address holds one, however the forwarding was accepted.
  • mail settings forwarding create was refused by Proton every time. The key it published was not shaped like the ones Proton’s own clients write.
  • A session refresh that never reached Proton exits 5 rather than telling you to sign in again over a network that was down.
  • A dry run of drive items upload --if-exists failed as a bug, and an upload destination that turns out to be a file now reads as a mistake rather than a bug.
  • Six settings set examples in --help named keys that do not exist, mail signature and drive revision-retention among them.
  • Breaking. --page-size 0 on a mail listing returns the whole folder rather than the first 25, and a size above 150 is filled rather than cut short. Affects mail messages list, mail conversations list and mail drafts list.
  • Breaking. --limit 0 on a mail bulk verb lifts the cap rather than acting on the first 25; the default is still 150.
  • Breaking. A negative --page, --page-size or --limit is refused, as is --page beside --page-size 0.
  • A mail bulk verb’s dry run and confirmation say when the selection filled --limit and more may exist.
  • proton skill states what the commands do and leaves how an agent should behave to you - whether it asks before a change is yours to set. It now covers paging and how an all-day event’s dates read.
  • pass items revisions list and revisions get reached only an item’s 50 most recent revisions.
  • proton skill prints the instructions an AI agent reads before using proton: what it is for, how to name things, which filters a listing takes, and to preview a change and ask before making it. It is written from the installed build, so it names exactly the commands you have. --body-only leaves the frontmatter off, for an agent that reads it as it runs rather than from a saved file.
  • calendar events create and update take --color, giving one event a colour of its own by accent name or hex; events get reports it. It colours what the reference names: one occurrence, --onwards for that one and every later one, or the whole series. A colour can only be changed, never taken off, and Proton’s own apps draw a per-event colour only on a paid plan.
  • calendar events export carries an event’s own colour as a COLOR property, and import reads one back as the nearest of Proton’s twenty accents, so a round trip keeps it.
  • contacts export writes each address’s groups as CATEGORIES, and contacts import files addresses into the groups a card names, creating any you do not have. --no-groups leaves them out.
  • --force-color, or FORCE_COLOR, paints even when the output is piped: a pager, a multiplexer, a CI log that renders the escapes itself.
  • Breaking. What you write decides whether a calendar event has a time of day. events create --start 2026-07-01 needs --all-day to agree, and --all-day beside a time of day or a duration under a day is refused. events update --start with a bare day moves the event and keeps its time of day instead of dropping it to midnight; --all-day=false gives one back, with a --start saying which time.
  • Breaking. --end on an all-day event is the last day it runs through, not the midnight after it. A script that added a day to compensate now books one day too many.
  • Breaking. calendar events create with neither --end nor --duration lasts as long as its calendar says a new event lasts, rather than a fixed hour. calendar settings calendars get shows that length.
  • Sending stops when a recipient’s contact cannot be read, instead of encrypting to whatever key Proton hands back. A contact that will not open cannot say whether it pins a key. Affects mail messages send, reply, forward and mail drafts send.
  • Every change says when the run could not read part of what it acted on - in the confirmation, the dry run, the result, and as skipped under --output json - the way a listing already does.
  • contacts update, merge, keys pin and unpin save over a card whose signature does not verify and say so. update refused outright and the others said nothing; a detached signature cannot tell tampering from a retired key.
  • calendar settings calendars get and contacts groups get show a colour as its swatch and name, which their listings already did.
  • An all-day calendar event could not be given a time of day back: a --start naming one was accepted, discarded, and reported as a success.
  • Colour in a Windows console. Nothing there sets TERM, so every screen was drawn plain.
  • pass invitations accept took the server’s word for who sent an offer. The key an offer carries is now checked against the keys Proton publishes for the address it names as sender, and one nobody signed is refused; pass invitations list leaves a vault’s name blank rather than reading it from such a key.
  • A vault key written here is sealed to your primary user key alone, as Proton’s own client seals it. Every key the account ever had could open one created or accepted before, retired ones included.
  • contacts create, update, import and merge were refused with Invalid PGP packets on an account holding more than one user key. A contact card is now sealed to your primary user key alone, which is what Proton’s own clients send.
  • contacts update and contacts merge added a second copy of the address and of every pinned key each time they ran. contacts export and contacts keys list showed one copy per past edit; running contacts update on an affected contact once more clears it.
  • mail settings autoreply enable and disable take --password-file, --password-stdin and --totp. Proton guards the switch whichever way it goes, so both failed outside a terminal.
  • proton mail settings forwarding hands mail arriving at one of your addresses to another Proton account, end-to-end encrypted: list, get, create, delete, enable, disable, resend. Accepting a forwarding somebody sent you is not built - that happens in a Proton client.
  • An account or recipient using post-quantum encryption now works instead of exiting 8. Signing in, sending, and sharing a calendar, file or vault all read Proton’s ML-DSA and ML-KEM keys. proton still never creates one.
  • Sharing with an address outside Proton exits 1 with a sentence naming the address, instead of 4 carrying Proton’s own error. Affects calendar settings calendars share add, pass vaults share add and drive items share add.
  • proton report links the bug form directly and no longer asks in its own words for what the form already asks for.
  • proton report says where your settings came from rather than the file’s path, which holds your home directory.
  • The go install row from the install instructions. It resolved to a version from before the command moved and installed nothing; use a package manager, the install script or a release binary.
  • Sharing with a Proton address whose key this build cannot read says so, instead of calling it an address outside Proton.
  • proton report carries the whole run. A report of a listing that made 750 requests held two lines. A run too large for an issue form is trimmed to its first and last records plus everything above debug between them, and says how many it left out; --dest writes all of it.
  • proton report runs on the defaults when the config file is too broken to parse, and says what was wrong with it. It was the one command that could not run in exactly the situation worth reporting.
  • Tab completion offers back what your listings showed: where a command takes a reference, the short ID and the subject, name or address beside it. It reads what this machine remembers rather than asking Proton, so a collection you have not listed yet offers nothing and says which listing would fill it.
  • Exit code 8 means the command reached a Proton feature this build does not implement. Nothing about the command was wrong, and neither a retry nor different arguments changes the answer.
  • A short ID printed by a get resolves in the next command. Only what a listing showed was remembered before.
  • An account or recipient whose keys use post-quantum encryption says so and exits 8. Signing in to such an account failed as a wrong password, and sending to one as an unreadable key.
  • proton report prints what a bug report needs: the build, your settings and a redacted trace of the run that failed. It needs no account and no network. --all takes every run still kept, --dest writes a file to attach.
  • Every run records what it did to ~/.config/proton-cli/logs/, one file per day, whatever --log-level says. Addresses, IDs and filenames are replaced by stand-ins before anything is written, so a log is safe to attach to an issue. The last 16 files are kept; --no-log, PROTON_NO_LOG or no-log: in the config file writes none.
  • Exit code 7 means the failure is a bug in proton rather than something you typed. Every failure you can act on keeps the code it had.
  • A crash prints one line pointing at proton report instead of a Go stack trace.
  • Refusals that arrived as bare text now say what to do: a pinned key that does not match the recipient’s, an inline attachment with no --html, a reply to a message with no reply address, an unknown pass items create --type, an alias in a Pass import, and a --remind that is not a warning time.
  • uninstall --purge also removes the diagnostic log.
  • A listing that cannot decrypt something says how many are missing instead of dropping them in silence, and --output json carries a skipped count.
  • “Failed to unlock any address keys.” now says which of four things went wrong, and the log records it per key.
  • --log-level debug shows what Mail, Drive, Calendar, Contacts and Pass log while they work. It never showed those lines before.
  • A config file. ~/.config/proton-cli/config.yaml holds what you would otherwise retype on every command or export as its own variable - output, profile, zone, quiet, log-level and the rest - with a per-profile: section for narrowing one to an account. It does not exist until you write it, and a key it does not recognise is an error rather than a line quietly ignored. --config and PROTON_CONFIG read a different one.
  • A confirmation policy says what proton must stop for and what it must not do at all: --confirm, PROTON_CONFIRM, or confirm: in the config file, scoped as narrowly as one command. A denied command exits 6 and nothing answers it - not --yes, not --dry-run, not a --confirm on the command line. Nothing you can write makes proton less careful than it is with no configuration at all.
  • Pass protected with an extra password can be reached at all: the first pass command asks for it, and Proton then lets the session reach Pass for as long as it lives. account login --extra-password-file or --extra-password-stdin hands it over for a run with nobody to ask.
  • pass items share add|get|remove|update shares one item and leaves the vault around it alone: what travels is the item’s own key, so they can open that item and nothing else.
  • pass vaults share update changes what somebody may do with a vault, and pass vaults transfer makes a member the owner of it.
  • pass shared list shows the items other people share with you, pass sharing list the ones you share. An item shared with you is in no vault of yours, so items list never had it.
  • pass items move REF --into VAULT puts an item in another vault, keeping its history. It gets a new ID, which the command prints.
  • pass items revisions get REF REVISION reads an earlier version back in full, and pass links get REF prints a link’s whole URL.
  • pass generate --words N makes a passphrase from Proton’s own wordlist, and --generate-password on pass items create|update makes the password on your machine and prints it beside the new ID, so a new login needs no file to read from.
  • --zone, and TZ beside it, set the one zone a run works in: what a written time means, what a listing prints, what an event is anchored to. Every event created or re-timed says which zone that was.
  • drive trash list shows each item’s name and when it was trashed, and sorts and pages like any other listing: --sort name|size|trashed, --desc, --page, --page-size.
  • --expires never removes an expiry, on a public link and on a message.
  • The changelog and a browsable Proton API reference are on the site. The reference is generated from the same openapi.yaml the repository holds, served at proton-cli.lerchster.dev/openapi.yaml for a code generator or an HTTP client to read.
  • Breaking. --output means the response format on every command. The commands that write bytes to disk take --dest and --dest-dir for where they go - every download, plus mail messages export, contacts export, calendar events export and pass export. A script passing a path to --output now fails, naming the three formats it accepts.
  • Breaking. A secret is never a flag value. pass items create|update no longer take --password, --totp-uri, --totp-field, --hidden, --number, --cvv, --pin or --private-key; use --secret-file NAME=FILE, --secret-stdin NAME or --generate-password. drive items share link --password is now --link-password-file / --link-password-stdin, with --clear-link-password to take one off. --eo-password on mail messages send, reply, forward and drafts send is now --eo-password-file / --eo-password-stdin.
  • Breaking. No Pass listing carries a secret, in any format. items list, aliases list, trash list and shared list used to print every password in the account under --output json; read one with items get, items revisions get or links get.
  • Breaking. --future is now --onwards, on calendar events update and calendar events delete.
  • Breaking. pass vaults share list is now pass vaults share get, and answers one record rather than a list.
  • Breaking. pass export holds the vaults you own, as Proton’s own export does. A vault somebody shared with you is theirs to back up; the command says on stderr how much it left out.
  • Breaking. An empty list under --output json and --output yaml is [] rather than a missing key, so jq '.attendees[]' iterates nothing instead of failing on null.
  • Breaking. A timed event whose zone cannot be named is refused rather than stored without one, and a clock reading that names two instants or none - the four hours a year the clocks move - needs an explicit offset instead of being settled in silence. 02:30 on a spring morning used to be stored as 03:30.
  • An occurrence count is exact or it is not given. “Every weekday forever” reported a thousand occurrences and deleting a series reported two hundred, both of them the cap the walk stopped at; occurrence_count is now absent where there is no number, and a change reaching a whole series shows what it would touch first.
  • A filtered drive items trash|delete|move|copy sends fifty items to a request rather than one, so a large selection finishes in a fraction of the round trips.
  • drive trash list shows everything drive trash empty deletes. Trashed photos sit on a volume of their own and were missing from the listing while empty destroyed them anyway, so the number you confirmed was not the number that went.
  • A filtered Drive command counts a folder and the files inside it once rather than twice - a folder is trashed, deleted, moved and copied whole - and names what Proton refused rather than reporting the count it hoped for.
  • proton api keeps stdout for the response. A proxy’s HTML error page standing in for the API’s answer arrived on stdout with exit 0, so a jq downstream broke while the pipeline looked like it had worked; it goes to stderr now and exits 5.
  • mail settings autoreply set refuses a schedule its --repeat mode does not allow before it reports anything, so --dry-run no longer predicts a change the real run would be refused for.
  • drive items download refuses a path that already exists before the transfer rather than after it.
  • A failed Drive block transfer no longer quotes the signed storage URL it was talking to. That URL is a credential for the block it names, and it was landing in error text that gets logged and pasted into issues. A redirect off the host Proton named is no longer followed with the storage token in hand.
  • Signing in with a security key. An account with one is asked to touch it, and one that also has an authenticator app reaches for the key when the code prompt is answered with nothing. The same answer satisfies the re-authentication guarding calendar settings calendars delete, mail messages expire and mail settings autoreply set, which an account with only a key could not give before.
  • A CAPTCHA can be solved on any device. proton prints the page, opens it in your browser where it can, and waits while you solve it - here, on your phone, anywhere. A machine with no display signs in like any other.
  • --verified TOKEN and PROTON_VERIFIED carry a verification solved out of band, for a run that cannot wait for one. A run that may not ask - --no-input, or no terminal - now prints the page and the token to repeat the command with, so a scheduled job can hand the link to a person and try again.
  • A CAPTCHA opens in your own browser rather than a window proton draws. Nothing to install on Linux: libwebkit2gtk, libgtk-3 and glib-networking are no longer needed, the .deb and .rpm packages no longer depend on GTK or WebKit, and a go install build verifies exactly as a release binary does.
  • The embedded CAPTCHA webview helper, and PROTON_HV_HELPER with it. Your browser does the job and there is no helper left to point at.
  • Accounts in Proton’s two-password mode can be used at all: account login asks for the second password once the sign-in is through, or reads it from --second-password-file or --second-password-stdin. Such an account used to sign in and then fail on every key it tried to open.
  • account settings get says whether an account keeps the password that signs it in apart from the one that opens its data, as a Two-Password Mode row and as two_password_mode.
  • Every command, argument and flag has a reference page at proton-cli.lerchster.dev, generated from the same command tree --help prints from.
  • A --help screen ends with a link to that command’s page instead of repeating the global flags, which are listed on proton --help alone.
  • A short ID starts after the dash an ID may begin with, so nothing a listing shortens can be read as a flag when it is pasted back. About one ID in sixty-four begins with one, and a short ID copied for such a thing before this release needs listing again.
  • Keys that do not open name the secret that was refused - Incorrect second password. in two-password mode - rather than coming out as a decryption failure.
  • An ambiguous reference lists its candidates as short IDs, unless shortening them would print the same token twice.
  • Installing an older version from the APT repository works again: it carries the last ten releases rather than only the newest, so apt install proton-cli=2.8.0 and a rollback find something to install.
  • The key password kept with a session is a cache: one that has stopped opening the keys is worked out again instead of leaving a profile that decrypts nothing, which is what a password changed elsewhere used to leave behind. Signing in drops the one a previous session left, so account get no longer calls such a profile unlocked.
  • A command that needs your password but takes no --password-file points at proton account login, instead of naming a flag it would reject.
  • Messages say which thread they belong to: conversation_id is on every message under --output json and --output yaml, from mail messages list, watch and get. Acting on the whole thread no longer costs a second lookup.
  • calendar reminders list and calendar reminders watch report the event a reminder warns about in full - its location, description, attendees and the rest sit on the row beside fires, remind and says. reminders list gains a LOCATION column, so a script reading that text output by column position will shift.
  • Signing in survives a bad moment at Proton’s edge: a 502 or a dropped connection is waited out and asked again, instead of coming out as a complaint about JSON and exit 1. Only what spends nothing is ever asked twice - a two-factor code least of all.
  • A sign-in refused for a wrong password or two-factor code exits 2, and one that outlasts the waiting - a rate limit included - exits 5, so a scheduled job can tell “fix the password” from “come back later”.
  • mail messages watch prints a line the moment a message arrives and stays attached until you stop it. Without --folder it covers the inbox and every folder set to notify, and a thread coming back from snooze counts as arriving.
  • calendar reminders list says which reminders fall due between two dates, and calendar reminders watch prints each one at the second it is due. When a reminder fires is Proton’s own answer, so an all-day event reminds at the hour its calendar chose rather than at midnight.
  • --notify on mail settings folders create and mail settings folders update says whether mail landing in a folder is worth being told about. It is what mail messages watch covers when you name no folder.
  • A watch’s output is a line per thing rather than a collection: under --output json each line is one object instead of an envelope with a count, and under --output yaml each thing is its own document. Stopping one with Ctrl+C or SIGTERM exits 0.
  • mail settings folders list shows a NOTIFY column, and a folder carries notify in JSON. A script reading that text output by column position will shift.
  • Calendar sharing: calendar settings calendars share add|list|remove hands a calendar to another Proton account, and calendar invitations list|accept|decline is the other side of it.
  • calendar settings calendars create --url subscribes to a calendar published elsewhere, and calendar settings calendars get shows one in full.
  • calendar events export and calendar events import PATH read and write .ics files. An import is addressed by each event’s UID, so exporting, editing and importing back is a restore rather than a second copy.
  • Events take --end, --status, optional attendees, and reminders that email rather than notify.
  • contacts export and contacts import PATH read and write vCard files, addressed by UID the same way.
  • contacts merge folds duplicate contacts into one, and contacts groups get shows which addresses are in a group.
  • Contact values can say what kind they are - --email work:jane@acme.com - and eleven more fields are settable, among them the organization, the title and the birthday.
  • Vault sharing: pass vaults share add|list|remove and pass invitations list|accept|decline.
  • pass export and pass import PATH write and read a Proton Pass archive, the same zip the web client produces. Without a passphrase the archive is not encrypted, and says so as it writes.
  • pass links create|list|revoke shows one item to somebody without a Proton account, for a while or a number of views.
  • pass breaches list|get reports which of your addresses have turned up in somebody else’s data breach.
  • pass aliases contacts create|list|block|allow|delete gives an alias an address per correspondent, so a reply leaves as the alias instead of the mailbox behind it.
  • pass settings mailboxes list|create|verify|resend|update|delete manages where aliases forward, and pass settings domains list shows what an alias can be made on.
  • pass items revisions list, pass items pin|unpin, pass items totp, pass vaults get, and pass generate for a password.
  • Items take custom sections: --field SECTION/NAME=VALUE, --hidden for a secret one, and --totp-field for a second code. An identity takes all 31 of its fields, not 13.
  • pass vaults create and update take --description, --icon and --color.
  • mail settings filters create --if "subject contains invoice" --move-to Archive describes a filter in conditions and actions and lets Proton write the Sieve; --sieve still takes a script of your own. filters get shows an existing one in the same words, and filters update rewrites a rule in place, keeping its position in the order.
  • mail settings filters apply runs filters over mail already in the mailbox, and filters reorder sets the order they run in.
  • mail settings senders list|block|allow|spam|forget manages the block and allow lists.
  • mail conversations snooze|unsnooze takes a thread out of the inbox until a time you choose, and snoozed is addressable wherever a folder is - as are the categories Proton sorts into: social, promotions, newsletters, transactions and updates.
  • mail messages expire makes a message delete itself later, mail messages unsubscribe asks a mailing list to stop, and mail messages empty --folder empties a folder outright.
  • Five more mail settings: next-message-on-move, pgp-scheme, remove-image-metadata, right-to-left and spam-action.
  • drive shared list shows what other people have shared with you and drive sharing list what you have shared. drive items share update|resend changes or resends an invitation, and drive photos albums update --cover sets an album’s cover.
  • PROTON_HV_HELPER names a CAPTCHA helper to run instead of the built-in one, which is what lets a go install build verify at all.
  • --passphrase-file and --passphrase-stdin hand over a passphrase without putting it in the command line.
  • Breaking. mail messages search and mail conversations search are gone. mail messages list and mail conversations list take the same filters; they were always one request to Proton.
  • Breaking. drive folders create is now drive items create, and drive share ... is now drive items share .... Update any scripts that call them.
  • The CAPTCHA window works on a Nix install. It opened blank saying “TLS support is not available”, because nothing supplied the TLS backend GIO loads as a module.
  • contacts create reports Proton’s refusal instead of exiting 0 having written nothing. Proton answers inside a successful response, and only the response was being read.
  • contacts groups add puts every one of a contact’s addresses in the group, as it always said it would, and --email acts on the address you named rather than one that happens to belong to another contact.
  • pass aliases options lists the domain an alias can be made on rather than a whole suffix. Proton mints the word in front of it afresh on every request, so what was listed had already stopped working by the time it could be typed back.
  • drive photos albums create prints the new album’s ID instead of nothing.
  • An install no package manager owns says when a release lands: once a day, under the command’s own output. PROTON_NO_UPDATE_CHECK=1 ends it, and a package-managed copy, a pipe and --quiet never see it.
  • proton changelog says what a release changed, including one you have not installed. --since and --until cover the ground between two.
  • Windows on ARM64 has its own build. winget, npm, the PowerShell installer and proton update hand an ARM64 machine a native binary; npm had none for it at all.
  • update --dry-run and uninstall --dry-run no longer ask you to sign in: they change this machine, not your account.
  • Tab completion answers again - every shell’s completion script was being refused as a mistyped command.
  • drive items upload --recursive sends a deep tree in fewer round trips.
  • drive folders create /a/b/c makes the folders above c instead of failing, and says how many it made.
  • drive folders create names the file standing in the path instead of failing with Link has no hash key.
  • The command is proton. proton-cli stays on your PATH as a second name, so nothing already written breaks and upgrading does not sign you out.
  • Breaking. go install github.com/roman-16/proton-cli/cmd/proton@latest - the path gained /cmd/proton. Every other way of installing is unchanged.
  • Colours are asked for by name, so output follows your terminal’s theme instead of overruling it. Swatches keep their exact colour.
  • drive items revisions download PATH REVISION_REF reads an earlier version out to disk or into a pipe, leaving the file as it is.
  • drive items revisions delete PATH REVISION_REF removes an earlier version permanently.
  • pass aliases disable REF and pass aliases enable REF stop and start an alias receiving mail without burning the address.
  • --if-exists replace|rename|skip on drive items upload answers a name Drive already has: a new revision, both under a numbered name, or nothing. Without it an upload still refuses.
  • pass items get and pass items update handle aliases as themselves: the address, whether it is on, where it forwards, --mailbox and --display-name.
  • pass aliases create prints the address Proton made, since it adds a word to your prefix.
  • Single-letter forms for the global flags: -p, -o, -n, -q, -y. They cluster, so -qn is a quiet dry run.
  • A caveat prints as its own ! line on stderr, rather than as commentary above the green tick.
  • Breaking. An unrecognised reference exits 3 (not found) rather than 1. Update scripts that read 1 as “no such thing”.
  • Breaking. A bad command line exits 1 (user error) rather than 2 (authentication), even when signed out.
  • Colour marks only what carries a verdict or a colour of its own; every verdict is still spelled out in words.
  • Label, folder, calendar and group lists show a colour as its swatch and name rather than a hex code.
  • The FLAGS column counts attachments instead of drawing 📎, which no monospace font sizes correctly.
  • Tables measure width in terminal cells, so CJK subjects and emoji filenames stay aligned.
  • An empty result reads No messages match. after a filter, rather than No messages.
  • Transfers show speed and time left, and number themselves within a batch ([3/27]).
  • Commands send independent requests together and never fetch the same thing twice, so they finish in fewer round trips.
  • Commands that decrypt no longer wait out the key unlock before they start.
  • --app-version and PROTON_APP_VERSION. Nothing needed them.
  • IDs that begin with - are no longer read as flags, in full, short and SHARE/ITEM references.
  • drive items revisions restore reports success instead of an error when Proton accepts it.
  • A recursive upload meeting a file where a folder must go is refused before anything is written, not part way through.
  • A command that needs a feature your plan lacks says so, instead of retrying and losing the reason.