A macOS 13+ status-bar app that wraps OpenConnect for Cisco AnyConnect-compatible VPNs, generates the TOTP one-time code, and lives quietly in your menu bar. Universal Binary, ad-hoc signed, no Developer ID required.
⚠️ This is a generic OpenConnect front-end. The defaultgateway(vpn.example.com) andserverCertPinbaked intoVPNConfig.swiftare placeholders. You provide your own VPN's hostname, certificate pin, and TOTP secret during the Onboarding wizard (or later in Settings → Advanced).
- One-click in-app dependency installer — the Onboarding wizard's Dependency Check shows a Fix button next to every red item:
- Install Homebrew — opens Terminal with the official installer pre-typed
- Install openconnect — runs
brew install openconnectinside the app, with live progress - Configure sudoers NOPASSWD rule — one TouchID via macOS authorization writes the rule to
/etc/sudoers.d/ - Reset arch-mismatched vpnc-script path — auto-corrects when an Intel default is loaded on Apple Silicon (or vice versa)
- Architecture-aware — Universal Binary; auto-detects Apple Silicon vs Intel and uses the right Homebrew prefix (
/opt/homebrewvs/usr/local) - TOTP built in — RFC 6238 HMAC-SHA1 via CryptoKit, with its own Base32 decoder. Optional QR-code import from a screenshot file
- Auto-reconnect on network change — drops the VPN cleanly when WiFi disconnects, reconnects automatically when it comes back (only if you connected manually — failed connects don't loop-retry)
- Stale host-route cleanup — scrubs leftover routes from the previous WiFi gateway before each connect, fixing the "Failed to connect after WiFi switch" symptom
- Status-bar agent —
LSUIElement, no Dock icon, no notification spam - Auto-update — checks GitHub Releases for new versions via Sparkle. One-click update from the menu bar
A ready-to-run VPNMenuBar.app is committed in this repo (Universal Binary, ad-hoc signed):
git clone https://github.com/CoderZCC/VPNMenuBar.git
cp -R VPNMenuBar/VPNMenuBar.app /Applications/
xattr -dr com.apple.quarantine /Applications/VPNMenuBar.app
open /Applications/VPNMenuBar.appbrew install xcodegen
git clone https://github.com/CoderZCC/VPNMenuBar.git
cd VPNMenuBar
xcodegen generate
xcodebuild -project VPNMenuBar.xcodeproj -scheme VPNMenuBar \
-configuration Release -destination 'platform=macOS' build
# The built .app is under ~/Library/Developer/Xcode/DerivedData/...
# See CLAUDE.md "Build, run, ship" for the full distribution recipe.Download the latest VPNMenuBar-x.y.z.zip from the Releases page:
- Unzip → drag
VPNMenuBar.appto/Applications xattr -dr com.apple.quarantine /Applications/VPNMenuBar.app- Open the app — subsequent updates will be delivered automatically via the menu bar
Right-click the app in Finder → Open → Open in the Gatekeeper warning. After that, normal launches work and the login-item registration happens automatically.
The Onboarding wizard walks you through:
- Welcome
- Dependency Check — Homebrew, openconnect, sudoers rule, vpnc-script. Click Fix on any red row to auto-install
- Credentials — username, password prefix (from your VPN admin), TOTP secret (Base32 — optional QR-image import button)
- Done
For the detailed walkthrough including how to extract a TOTP Base32 secret from a QR code image, see INSTALL.md (中文).
If you skip the in-app fix and configure sudo manually, the NOPASSWD rule needs three commands. Run sudo visudo and append (replace <your-user> with your macOS username):
Apple Silicon:
<your-user> ALL=(root) NOPASSWD: /opt/homebrew/bin/openconnect, /usr/bin/pkill -x openconnect, /sbin/route
Intel:
<your-user> ALL=(root) NOPASSWD: /usr/local/bin/openconnect, /usr/bin/pkill -x openconnect, /sbin/route
The third entry (/sbin/route) lets the app clean stale host routes after a WiFi switch — without it you'll see "Failed to connect" errors after roaming networks.
Stored at ~/Library/Application Support/com.example.vpnmenubar/config.json with 0600 permissions. Plain JSON; contains your username, password prefix, TOTP Base32 secret, gateway, cert pin, and openconnect/vpnc-script paths.
config.json to git or share it — the TOTP secret is the seed for your one-time codes and is account-specific.
Single-target SwiftUI app with a strict one-way dependency graph:
UI → Core → Config / Dependencies
↘ Util
VPNController is the @MainActor ObservableObject state machine; all UI binds to its @Published state. Subprocess management is abstracted behind OpenConnectProcessRunning so the long-running sudo openconnect wrapper is testable in isolation.
See CLAUDE.md for the full architecture write-up, state machine details, sudoers rationale, quirks list, and distribution recipe.
Open issues or pull requests at https://github.com/CoderZCC/VPNMenuBar.