Skip to content
ZephyrSaiPublic

About

Tiny, unofficial menu-bar Proton VPN client for macOS. Picks the fastest server that isn't overloaded and switches automatically.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LeanVPN

A tiny, menu-bar-only Proton VPN client for macOS that does one thing: put you on the fastest server that isn't overloaded, and move you to another one when it fills up or stops answering.

Unofficial. LeanVPN is an independent project. It is not made, endorsed or supported by Proton AG. "Proton" and "Proton VPN" are trademarks of Proton AG. You need your own Proton account. Use it at your own risk (see Disclaimer).

Status: alpha. It works daily on the author's Mac, but it has been tested on exactly one machine. See Status and testing before relying on it.

Website: https://zephyrsai.github.io/LeanVPN/

Why

The official app is a 233 MB, full-featured client. My real problem was simpler: servers got congested and the connection kept dropping, so I was constantly switching servers by hand. LeanVPN picks a good server by itself, keeps checking it, and switches before you notice. It has no window, no settings, no country list. Just a shield in the menu bar.

Footprint

Measured on an M-series MacBook Pro, macOS 26 (memory = Activity Monitor's "Memory" column):

LeanVPN Proton VPN 6.5.1
On disk 6.1 MB (app 124 KB + helper 6 MB) 233 MB
Menu-bar app memory 11–15 MB 168 MB
Background helper Only while connected; exits 30 s after you disconnect Always running
Idle CPU ~0% (one cheap counter check every 10 s) Noticeable over a day
Picking a server ~0.3 s (all candidates probed in parallel) n/a

Requirements

  • An Apple silicon Mac running macOS 13 Ventura or later
  • A Proton account. Free works (free servers only); paid plans can use every non-Secure-Core server.
  • Apple's Command Line Tools: xcode-select --install
  • Go: brew install go

No Xcode, no Apple developer account, no App Store.

Install

git clone https://github.com/ZephyrSai/LeanVPN.git
cd LeanVPN
make install

make install builds everything (about a minute the first time), then asks for your admin password once. That's needed to install the background helper that creates the VPN interface. The installer:

  • copies LeanVPN.app to /Applications and starts it (it also starts at login);
  • installs the helper to /Library/PrivilegedHelperTools/com.leanvpn.helper and registers it with launchd (/Library/LaunchDaemons/com.leanvpn.helper.plist). launchd only starts it when the app asks, and it quits again when the VPN is off.

Quit the official Proton VPN app first. Two VPNs at once fight over routes and DNS.

Sign in (one time)

LeanVPN never sees your password. It uses Proton's "enter a code on the web" sign-in, the same flow the Proton VPN Apple TV app uses.

  1. Click the shield in the menu bar, then Sign In…
  2. Your browser opens account.protonvpn.com/appletv and the 8-character code is already on your clipboard. Sign in to Proton there if asked, paste the code and approve.
  3. That's it. LeanVPN connects on its own a few seconds later.

If that page doesn't load (some networks block protonvpn.com), open the same address on your phone over mobile data and type the code shown in the LeanVPN menu. The code is valid for about 10 minutes.

Using it

Icon Meaning
Shield with a check Connected. Hover for server, city, load and latency.
Half-filled shield Connecting or switching servers
Shield with ! Waiting for network, or the last attempt failed (it retries by itself)
Plain shield Disconnected
Shield with a slash Signed out

The menu only ever shows what makes sense right now: Connect, Disconnect, Switch Server (jump to the next-best server), Retry Now, Sign Out, and Quit (which also turns the VPN off).

If you were connected when you shut down, LeanVPN reconnects after a restart.

How it picks a server

  1. Shortlist. Out of Proton's server list (filtered to your plan, skipping Secure Core, Tor, restricted servers and servers in maintenance): the 10 best by Proton's own score (distance + load from where you are), plus the 6 least-loaded of the 60 nearest.
  2. Measure. Real latency to every candidate at once: a TCP connect to each server, sent over your normal connection, never the tunnel. About 0.3 s in total.
  3. Score. Lowest latency_ms + 300 × (load/100)² wins. Load hardly matters when it's low, but it dominates as a server nears saturation. A 40 ms server at 90% load loses to a 90 ms server at 30% load.
  4. Verify. A WireGuard handshake isn't proof a server works (overloaded servers often complete one), so LeanVPN also needs an answer from Proton's in-tunnel DNS resolver before it calls itself connected. If there's no answer, it tries the next server, up to 4, rotating through Proton's alternative WireGuard ports in case your network blocks the default one.

How it stays connected

Every 10 seconds the helper reads two counters from WireGuard. That costs almost nothing.

  • Stalled (sending but receiving nothing for 20 s), or no handshake for 200 s: it asks the in-tunnel resolver. No answer twice means it switches to the next-best server, and avoids the dead one for 30 minutes.
  • Every 15 minutes: it refreshes server loads. If yours is 85% full or more and one at least 20 points lighter exists, it moves. A server entering maintenance is left immediately.
  • Network changes (Wi-Fi ↔ Ethernet, sleep/wake, DHCP renewals): it re-pins the server route, rebinds the socket and re-applies DNS.
  • Certificates and sign-in tokens are renewed automatically. If Proton's API can't be reached, a still-valid certificate and the cached server list are enough to connect.
  • If switching fails, the tunnel is taken down rather than left routing your traffic nowhere, and LeanVPN keeps retrying (immediately when your network comes back).
  • If the helper ever crashes, launchd restarts it; it restores your DNS and reconnects.

Privacy and security

  • No password ever passes through LeanVPN. Session tokens and the WireGuard key are stored in /Library/Application Support/LeanVPN/, readable by root only.
  • The helper only accepts commands from your user account (and root).
  • LeanVPN only talks to vpn-api.proton.me and the VPN servers. There's no telemetry, crash reporting, analytics or update check.
  • While connected, all IPv4 traffic goes through the tunnel and DNS goes to Proton's resolver (10.2.0.1). IPv6 is blocked: the tunnel only carries IPv4, so IPv6 would otherwise leak around it.
  • There is no kill switch. If the tunnel goes down, your traffic goes out normally until LeanVPN reconnects (usually within seconds). Use the official app if you need a hard kill switch.
  • It identifies itself as Proton's Apple TV app. Proton's code-entry page only approves codes for that app, so LeanVPN uses that app's identifier to sign in.
  • Restrictive networks. Some networks silently drop the TLS handshake to Proton's API. LeanVPN sends the first handshake packet in two pieces, which gets it through.

Troubleshooting

Live logs:

/usr/bin/log stream --predicate 'subsystem == "com.leanvpn"' --level info

Past hour (connects, switches, failures):

/usr/bin/log show --last 1h --predicate 'subsystem == "com.leanvpn"'
Problem Fix
Menu says "Helper not installed" Run make install again
Sign-in page is blank Your network blocks protonvpn.com. Open the page on your phone over mobile data.
"Sign-in failed" Try Sign In… again. If it keeps failing, check the logs.
No internet after a crash or force-quit Reconnect, or run sudo /Library/PrivilegedHelperTools/com.leanvpn.helper --cleanup to restore your DNS
Keeps switching servers Look in the logs for leaving … lines, which give the reason each time
Conflicts with another VPN Disconnect the other VPN (the official Proton app, corporate VPNs) first

Uninstall

make uninstall

This disconnects, restores your DNS, and removes the app, the helper, the launchd entry and the saved sign-in. Run it from the cloned folder (or run sudo Support/uninstall.sh).

Status and testing

Alpha. What has actually been verified, on one Apple silicon Mac with a Proton Free account:

  • Sign-in with the code flow, connecting, server ranking by location, failover when a server doesn't carry traffic, and reconnecting automatically after the helper restarts.
  • IPv4 routing through the tunnel, the server route going via the real gateway, DNS through Proton, IPv6 blocked, and the exit IP being Proton's. About 40 Mbit/s through a free server.
  • Footprint and idle CPU as in the table above.

Not yet tested enough: long sessions (days), sleep/wake cycles, switching between Wi-Fi, Ethernet and phone tethering mid-session, captive portals, certificate and token renewal over long sessions, paid plans, macOS 13–15, and other people's Macs and networks. Bug reports are very welcome, especially with the output of the /usr/bin/log show command above.

Deliberately not included

Kill switch, country or server picker, Secure Core, NetShield, split tunneling, port forwarding, profiles, notifications, settings, auto-update, Intel builds. Keeping it small is the point.

Project layout

Path What
Sources/App/main.swift The menu-bar app (AppKit, about 190 lines)
Sources/Helper/Engine.swift Session, server choice, health checks and failover
Sources/Helper/Tunnel.swift utun interface, routes, DNS, network watcher
Sources/Helper/Servers.swift Server list parsing, shortlist and latency probe
Sources/Helper/API.swift The handful of Proton API calls
Sources/Helper/bridge.h Small C helpers: utun socket, TCP and DNS probes
wg/ wireguard-go (vendored) built as a static library, plus the HTTPS client and its tests
Support/ Installer, uninstaller, launchd plist, Info.plist
docs/ The website (GitHub Pages)

Build without installing: make (outputs go to build/). Run the tests with make test.

Disclaimer

LeanVPN is provided as is, without warranty of any kind. It is not affiliated with Proton AG. It uses Proton's API the way Proton's own apps do, including the Apple TV app's identifier for sign-in. Proton could change or block that at any time, and using an unofficial client may be against Proton's terms of service. You are responsible for how you use it.

License and credits

LeanVPN is free software under the GNU General Public License v3.0.

  • WireGuard engine: wireguard-go (MIT), with a bridge trimmed from wireguard-apple's api-apple.go (MIT). The vendored modules keep their own licenses under wg/vendor/.
  • API behaviour follows Proton's open-source apps (ProtonVPN/ios-mac-app, GPLv3).

About

Tiny, unofficial menu-bar Proton VPN client for macOS. Picks the fastest server that isn't overloaded and switches automatically.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages