@alexsarrell/occ
v0.7.2
Published
OpenConnect VPN CLI for macOS with a rich terminal UI
Maintainers
Readme
@alexsarrell/occ
OpenConnect VPN CLI for macOS with a rich terminal UI. Native AnyConnect-quality behaviour without the bloat: Keychain-backed credentials, Touch ID for sudo, auto-fill OTP codes, sane DNS handling, and zero leftover state when you disconnect.
$ occ connect work
🟢 connected → work (vpn.example.com) q · Esc disconnect Tab logsInstall
brew install openconnect # required runtime dependency
npm install -g @alexsarrell/occ
occ doctor # verify everything is wired upRequires macOS, Node.js ≥ 18, openconnect.
Quick start
occ # first run: walks you through profile setup
occ connect # connect to default profile
occ connect work # connect to a named profile
occ stop # disconnectFeatures
Profiles
Multiple VPN endpoints, one default. Stored as plain JSON in ~/.occ/profiles.json,
passwords go to macOS Keychain only.
occ profiles add # interactive wizard
occ profiles list
occ profiles default britain # set defaultTouch ID for sudo
occ connect needs root to create the tunnel. Instead of typing the password every
time, enable Touch ID via PAM:
occ touchid enable # adds pam_tid.so to /etc/pam.d/sudo_local
# also offers to install pam_reattach so Touch ID
# works in iTerm / tmux / pty (not just Terminal)Once enabled, every sudo prompt — including occ connect — accepts the fingerprint
sensor. Password fallback still works.
Auto-fill OTP from Keychain
If your VPN requires a TOTP second factor, import the secret once and occ connect
will generate codes automatically. No more juggling Google Authenticator on your
phone every time you connect.
# 1. Export from Google Authenticator on your phone:
# menu → Transfer accounts → Export → save the QR
# 2. Decode the QR (Photo Booth + zbar, or any QR reader)
occ totp import 'otpauth-migration://offline?data=...'
occ totp show work # current code (debug)
occ totp uri work --qr # render a QR to import the same secret into
# Apple Passwords / Authy / Raivo / 1PasswordBuilt on otpauth — supports SHA-1/256/512,
6/8 digits, arbitrary period.
Global hotkeys
Connect / disconnect VPN from anywhere via skhd:
occ hotkeys install # ⌃⌥⌘C connect, ⌃⌥⌘D disconnect, ⌃⌥⌘V menuSane DNS handling (no leftovers)
The bundled vpnc-script writes only to scutil's Dynamic Store
(State:/Network/Service/<utun>/...). Persistent network preferences in System
Settings are never touched. So even on an ungraceful exit (kernel panic, battery
loss, force-kill), your Wi-Fi DNS stays intact.
For older installs that hit DNS zombies before this design, there's a safety net:
occ heal # one-shot fix
occ heal install # LaunchAgent — runs on every login
occ clean # nuke DNS to DHCP defaultsArchitecture
- TypeScript + Ink (React-in-terminal)
node-ptyfor openconnect interactionotpauthfor TOTP- macOS Keychain (via
securityCLI) for secrets - Bundled vpnc-script using
scutilDynamic Store for split-DNS without persistence
Configuration
| Path | Purpose |
|---|---|
| ~/.occ/profiles.json | VPN profile definitions (plain JSON, edit by hand if you want) |
| ~/.occ/vpnc-script.log | What the bundled vpnc-script did during connect/disconnect |
| macOS Keychain (openconnect service) | VPN passwords |
| macOS Keychain (occ-totp-* services) | TOTP secrets |
Development
git clone https://github.com/alexsarrell/occ.git
cd occ
npm install
npm run build
npm link # use your local build as the global `occ`
npm test # vitest