Updated January 15, 2026
You cannot use simple command line clients to send email to Gmail or Yahoo domains because of the extra authentication that is required [1][2].
One of the features of Proton Mail is that you can send emails through their SMTP servers without loggin in through a web browser. This feature is only available for business accounts.
Proton mail refers to this as SMTP Submission
Their most economical plan for doing this is "Mail Professional" ($10/month).
The "Simple" Mail Transfer Protocol (SMTP) is not that simple. To reduce the complexity go between programs were developed such as ssmtp, msmtp and swaks.
Unforunately, With these programs, comes limiations. For example, ssmpt only support two authentification methods: LOGIN and CRAM-MD5.
Proton's smtp-submission requires that the authentification method be PLAIN.
Furthermore, ssmtp has not been maintained since 2019. Debian suggests using msmtp as its replacement. However, I found it to be more complex than swaks.
However, because of spammer, the authentification for logging into one of these servers via SMPT has become complex.
swaks stands for: Swiss army knife for smtp
The following is from reference [1] below:
swaks \
--from sender@example.com \
--to recipient@example.com \
--server send.ahasend.com \
--port 587 \
--auth plain \
--tls \
--auth-user 'YOUR_SMTP_USERNAME' \
--auth-password 'YOUR_SMTP_PASSWORD' \
--header 'Subject: Test email' \
--body "This is a test email."
Substituting my values into this example:
swaks \
--from ray@franco.ms \
--to ray@franco.ms \
--server smtp.protonmail.ch \
--port 587 \
--auth PLAIN \
--tls \
--auth-user 'ray@franco.ms' \
--auth-password 'XM42XFR6G2QC49QL' \
--header 'Subject: Test email' \
--body "This is a test email."
Got me past the authentification, but it gave me the following error:
~> AUTH PLAIN AHJheUBmcmFuY28ubXMAWE00MlhGUjZHMlFDNDlRTA== <~ 235 2.7.0 Authentication successful ~> MAIL FROM:<~ 250 2.1.0 Ok ~> RCPT TO: <~* 501 5.5.2 <50>: Helo command rejected: Invalid name ~> QUIT <~ 221 2.0.0 Bye === Connection closed with remote host. ./swak.sh: line 11: --header: command not found
Note that <50> in the error message is my hostname for the host I ran the command on.
After adding a --ehlo option (flag), I was able to successfully send an email to myself through Proton Mail's smapt server. See below:
wa@50:~ $ cat swak.sh
swaks \
--from ray@franco.ms \
--to ray@franco.ms \
--server smtp.protonmail.ch \
--port 587 \
--auth PLAIN \
--tls \
--auth-user 'ray@franco.ms' \
--auth-password 'XM42XFR6G2QC49QL' \
--ehlo 127.0.0.1 \
--header 'Subject: My 2nd Email' \
--body "This is my 2nd test email."
wa@50:~ $ ./swak.sh === Trying smtp.protonmail.ch:587... === Connected to smtp.protonmail.ch. <- 220 mailsub002.protonmail.ch ESMTP Postfix -> EHLO 127.0.0.1 <- 250-mailsub002.protonmail.ch <- 250-PIPELINING <- 250-SIZE 36480000 <- 250-STARTTLS <- 250-ENHANCEDSTATUSCODES <- 250-8BITMIME <- 250 CHUNKING -> STARTTLS <- 220 2.0.0 Ready to start TLS === TLS started with cipher TLSv1.3:TLS_AES_256_GCM_SHA384:256 === TLS no local certificate set === TLS peer DN="/CN=protonmail.com" ~> EHLO 127.0.0.1 <~ 250-mailsub002.protonmail.ch <~ 250-PIPELINING <~ 250-SIZE 36480000 <~ 250-AUTH PLAIN <~ 250-ENHANCEDSTATUSCODES <~ 250-8BITMIME <~ 250 CHUNKING ~> AUTH PLAIN AHJheUBmcmFuY28ubXMAWE00MlhGUjZHMlFDNDlRTA== <~ 235 2.7.0 Authentication successful ~> MAIL FROM:<~ 250 2.1.0 Ok ~> RCPT TO: <~ 250 2.1.5 Ok ~> DATA <~ 354 End data with . ~> Date: Thu, 03 Apr 2025 14:25:07 -0500 ~> To: ray@franco.ms ~> From: ray@franco.ms ~> Subject: My 2nd Email ~> Message-Id: <20250403142507.060868@50> ~> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/ ~> ~> This is my 2nd test email. ~> ~> ~> . <~ 250 2.0.0 Ok: queued as 4ZTBX528Csz44V ~> QUIT <~ 221 2.0.0 Bye === Connection closed with remote host
This worked !!!
Watchdog timers require counter. You can use a /dev/shm/file to store the count. For example:
echo 0 > /dev/shm/counter
Will create the file, counter, whose contents is zero.
You can then read the file counter into a varable, increment it, and write it back to the file, counter.
var=$(</dev/shm/counter)
echo $[$var+1] > /dev/shm/counter
Proton Calendar does not have a way to mark events as busy or free. My work around for this is to have two calendars one for busy and another for free. The busy events are red, and the free events are green. When you create an event, you can select which calendar to place the event in. In addition, I also have a third calendars to keep up with my wife, which is brown.
Proton Calendar allows you to share your calendar-data with anyone by creating a public link to your calendar's data. Clicking on the link downloads a calendar.ics file (ics - internet calendaring and scheduling).
You can create a link to a full view calendar.ics or a limited view calendar. A limited view calendar shows only when you are busy, without event details.
A calendar.ics is just a text file that can be read by a text editor or the Linux cat command. To view the data in a calendar you must import the ics file into a calendar rendering program (e.g. Google Calendar, Outlook Calendar, GNONE Calendar, etc.). You can import more than one ics file into the same calendar rendering program.
In HTML, you paste a url (link) into an anker-href statement and let the user click on the link to download a file.
In bash, it is more complicated. You use a web browser and the calendar-data link provided by Proton to generate a curl (client for url) command.
The steps depend on which web browser you are using.For the FireFox browser:
This will result in a Linux curl command being copied to the the clipboard. Paste this command into your script file.
For example, the link to my Limited View Busy Calendar-data provide by Proton was:
https://calendar.proton.me/api/calendar/v1/url/0lTcLOr_GkvsbbQiqbjwFJIGzfQzvEuC3JyDX1QzC6DJM80Gaqf0sxu9vX0qcEgA6GXNqTNAvxXy7nSe5I6ZXg==/calendar.ics?CacheKey=1FsehvWfOQ5hDzQ8-AedPg%3D%3D
and the curl command generated by the Firefox browser was:
curl 'https://calendar.proton.me/api/calendar/v1/url/0lTcLOr_GkvsbbQiqbjwFJIGzfQzvEuC3JyDX1QzC6DJM80Gaqf0sxu9vX0qcEgA6GXNqTNAvxXy7nSe5I6ZXg==/calendar.ics?CacheKey=1FsehvWfOQ5hDzQ8-AedPg%3D%3D' \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
-H 'DNT: 1' \
-H 'Sec-GPC: 1' \
-H 'Upgrade-Insecure-Requests: 1' \
-H 'Connection: keep-alive' \
-H 'Cookie: AUTH-fwjgufisza6sbyghnpes7lmebqbjmxgh=3fatoxqqotrddgcampn77lcgo5g4amii; Session-Id=aWejKrYjDk21VuTv9uMiLAAAAIY; iaas=WzJd; Tag=default; Domain=proton.me; st=eyJ0IjoicCIsInAiOiJtYWlsYml6MjAyNCIsImMiOjEyfQ' \
-H 'Sec-Fetch-Dest: document' \
-H 'Sec-Fetch-Mode: navigate' \
-H 'Sec-Fetch-Site: none' \
-H 'Sec-Fetch-User: ?1' \
-H 'Priority: u=0, i' \
-H 'TE: trailers'
If you past the above curl command, as is, into a bash script file, the output will by default go to screen (std out). To send the output of the curl command to a file use the -o option. That is: curl -o limited-busy-calender.ics 'https://calendar.proton.me/api/calen...'.
In my case I have four calendars:
and my bash script is four curl statements. One for each calendar.
The three full_view calendars are imported into one instance of a calendar rendering program for my viewing, and the limited_view_busy.ics file is imported into another instance of a calendar rendering program for sharing with others. The later is used for others to check my availablity when scheduling multi-party joint examinations.
On the Internet, I found a application, JS_Calendar, based on Java Script that would render ICS Calendar-Data. Unforntately, I know almost nothing about java script, but eventually I got to work.
The ics files to import into the calendar are at the very top of js/custom_display.js. That is:
ics_sources = [
{url:'https://sogo.nomagic.uk/SOGo/dav/public/contact/Calendar/3D08-5CC47000-1-5EA59B00.ics', title:'Nomagic Calendar', event_properties:{color: 'SeaGreen'}},
{url:'https://nomagic.uk/calendars/gov.uk/events.ics', title: 'UK Bank Holidays in England and Wales', event_properties: {color: 'DodgerBlue'}},
{url:'https://nomagic.uk/calendars/gouv.fr/events.ics', title: 'French Bank Holidays in Metropole', event_properties: {color: 'DeepPink'}}
]
change to:
// Edit your ics sources here
ics_sources = [
{url:'https://eleix.com/availability/limited_view_busy.ics', title: 'Availability', event_properties: {color: 'DeepPink'}},
{url:'https://eleix.com/calendar/sample.ics', title: 'Sample', event_properties: {color: 'SeaGreen'}}
To change the calendar view from British, with the first day of the week being Moday, to a US calencar, with the first day of the week being sunday find (around line 60):
firstDay: '1',
local: 'uk',
and change to:
firstDay: '0',
local: 'us',
To view the calendar with a small screen such as a cell phone, add the following line under the header title:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Find the h1 header:
MyEntitiy - Our selected events feeds
This appears above the calendar, change to to something more appropriate, such as:
Ray's Calendar
The first CORS issue was using a file for a URL. This causes a CORS error [4]. You must have an http server. You can set up a local http server using python [5]. However, this does not get you around the second issue. You must have permission from the http(s) file server to access the files. This can only be done if you own the http(s) file server. That is, you must modify the http file server to allow you to access to its files. This is done on Apache by placing a ".htaccess" file in the directory were the resources you want access to are located [6]. Note,to get this to work, I had to use single quotation marks and not the double quotation marks in [7].
I also observed that my calendar did not populate events on the iPhone or the Safari browser (MacOS). Part of this problem was with the server where my website is hosted. On MacOS, Safari showed it as insecure, while Firefox showed it as secure. This was causing CORS errors. Some places in the website inspector, it was reporting a CORS error to http and other places https. I had to call my web hosting service and get them to fix the problem on their end. Well, I had to call them several times. They got the secure part fixed, but then I had to type the prefix "https://" into the address bar on my iPhone to get the calendar to show. Otherwise, it just went to my homepage. Eventually my web server provider did get this fixed on their end. Apparent even when everything is on one host Safari and iOS require it. That is, I not sure that I needed an ".htaccess" file for Windows or Linux.
According to Proton's website, if you share the calendar link with someone, they can import it into their calendar app and subscript to your calendar, which means they get updates. Is there some way to do this by code?
Be used to save all this code in a tar file on a NAS and/or on my website or on Proton's secure drive.
Proton has two Linux VPN apps. The main app has a GUI, and the CLI app has limited functionality.
Neither app supports logging in with a hardware security key. They are working on this.
The GUI app supports Debian and Ubuntu, but not the Raspberry Pi OS.
Although not officially supported, the CLI app appears to work with the Raspberry Pi OS.
Proton's VPN servers also support the official third party apps for OpenVPN and WireGuard.
If you have a paid Proton VPN plan, you can now contact support via live chat.
As of November 14, 2025, the latest release of Proton VPN Linux CLI is 0.1.2 [1]. As of January 4, 2026, the latest version is 0.1.3.
It currently only has five commands:
Usage: protonvpn [OPTIONS] COMMAND [ARGS]...
____ _ __ ______ _ _
| _ \ _ __ ___ | |_ ___ _ __ \ \ / / _ \| \ | |
| |_) | '__/ _ \| __/ _ \| '_ \ \ \ / /| |_) | \| |
| __/| | | (_) | || (_) | | | | \ V / | __/| |\ |
|_| |_| \___/ \__\___/|_| |_| \_/ |_| |_| \_| 0.1.3
Options:
-v, --verbose Show detailed output during command execution
-h, --help Show this message and exit.
Commands:
signin Sign in with Proton VPN credentials
signout Disconnect and remove credentials
info Display your Proton VPN account information
connect Connect to Proton VPN
disconnect Disconnect from Proton VPN
NEED HELP?
Report issues: https://protonvpn.com/support-form
With the free version, you cannot specify the country or city.
You cannot specify whether to use OpenVPN or WireGuard. When connected, there is a new Proton VPN entry under WireGuard. So, it is currently using WireGuard.
You cannot specify a kill switch.
You cannot whitelist IP addresses or ranges, but it does allow you to ssh and vpn to IP addresses on your subnet.
It appears to not use nftables nor iptables. Although at one time I saw a reference to nw chains in nftables.
I did face one problem with the Raspberry Pi OS. When signing in, it prompted me to enter a password for the keyring. Just leave the password blank and confirm that this is what you want, and it will not prompt you again for the keyring password [].
You sigin with:
protonvpn signin ray@franco.com
It prompts you for a password.
Enter your password, and enter:
protonvpn connect
You should now be connected to a server.
To disconnect and signout:
protonvpn disconnect
protonvpn signout
For some reason, the "protonvpn connect" command does not work over ssh. However, it does appear to work over VNC.
It also does not work if you are root: "sudo sh" or sudo protonvpn signin ray@franco.com.
According to Proton's technical support, protonvpn requires the gnone keyring. The Raspberry Pi OS does use the gonone keyring.
In theory, I should be able to write a cronjob script that connects at boot, and a script at /usr/lib/systemd/system-shutdown/ for shutting down or rebooting [3-6].
I created two script files:
cat protonvpn.exp
#!/usr/bin/expect -f
set password "redacted"
#exp_internal 1
spawn protonvpn signin ray@franco.ms
expect "Password:"
send $password\n
expect "$"
spawn protonvpn connect
expect "$"
expect eof
exit
and
cat test.exp
#!/usr/bin/expect -f
set password "redacted"
#exp_internal 1 # remove 1st # for troubleshooting
spawn protonvpn signin ray@franco.ms
expect "Password:"
send $password\r
expect {
"$ " { send_user "'$ ' prompt detected\n" }
"# " { send_user "'# ' prompt detected\n" }
eof { send_user "eof detected\n"}
timeout { send_user "expected timed out\n"}
}
spawn protonvpn connect
expect {
"$" { send_user "'$' prompt detected\n" }
"#" { send_user "'# ' prompt detected\n" }
eof { send_user "eof detected\n"}
timeout { send_user "expected timed out\n"}
}
expect {
"$ " { send_user "'$ ' prompt detected\n" }
"# " { send_user "'# ' prompt detected\n" }
eof { send_user "eof detected\n"}
timeout { send_user "expected timed out\n"}
}
Both script files will work, but they will not work if called from crontab, and it does not matter if you are root or not.
To disconnect and signout you can just use a bash script:
cat outsign.sh
#!/usr/bin/bash
protonvpn disconnect
protonvpn signout
Again the script will not work if you are root or over ssh or via a crontab.
Proton's VPN servers do support official third parts apps OpenVPN and WireGuard. To use these third parts apps, you must select a particular Proton VPN server and download a configuration file from Proton.
The disadvantages include:
As of 1/2/2026, Proton does not allow you to login to:
with a hardware security key such as Yubico.
You can however, log into https://account.proton.me/vpn.
Most of the links in the GUI app start with https://www.protonvpn.com. In addition, most of the links to download a configuration file for OpenVPN and WireGuard also start with https://www.protonvpn.com. However, you may be able to get to this page via https://account.proton.me/vpn.
As of December 24, 2023 [3], the GUI Linux app for Proton VPN is not compatible with the Raspberry Pi OS.
You can use the Command Line Interface (cli) Linux app, but it is still on version 3, while the GUI is on version 4. The current cli Linux app does not have an auto-connect feature, while the GUI version does offer auto-connect.
You have manually login:
protonvpn-cli login your_user_name
You then have to connect:
protonvpn-cli connect or just c
A console window will pop up, and you have to select the server country. Then, you must wait for almost one minute in the US, for the app to pole all the severs in the country to determine their availability and load. Some of the servers can stream videos and others cannot. After you select a sever, it will prompt you on whether to use UDP or TCP. Finally, it will connect and work as it should.
To disconnect:
protonvpn-cli disconnect or just d
To logout:
protonvpn-cli logout
To get help:
protonvpn-cli --help or -h
Proton's website says "Our new (v4) Linux doesn’t yet support a command line tool"[4]. Hopefully, this feature will be coming. According to a response in December 2023, at protonmail.uservoice.com, the cli version is planned.