A command-line interface for managing GlobalProtect VPN connections on macOS.
gp-vpn automates the GlobalProtect UI using AppleScript, providing a fast and scriptable way to manage your VPN connections from the terminal.
- CLI Commands: Connect, disconnect, check status, and toggle VPN connections
- Portal Shortcuts: Define shortcuts for frequently used portals in a config file
- Portal Discovery: Automatically discover configured portals from GlobalProtect Settings
- Interactive TUI: Terminal user interface with real-time status and keyboard navigation
- JSON Output: Machine-readable output for scripting and automation
- Logging: Configurable logging with zerolog
- macOS 14+ (Sonoma or later)
- GlobalProtect VPN client installed and running
- Terminal app with Accessibility permissions (see below)
gp-vpn uses AppleScript to automate the GlobalProtect UI. For this to work, your terminal application needs Accessibility permissions:
- Open System Settings > Privacy & Security > Accessibility
- Click the + button
- Add your terminal app (Terminal.app, iTerm2, Warp, etc.)
- Ensure the checkbox is enabled
- Restart your terminal app for the permissions to take effect
First Run: The first time you run
gp-vpn, macOS may show a prompt asking to allow your terminal to control "System Events". Click OK to allow this - it's required for AppleScript automation to work.
Tip: If you use multiple terminal apps (e.g., Terminal.app and VS Code's integrated terminal), each one needs its own Accessibility permission.
Download the latest release from the Releases page.
# Download and extract (example for arm64)
curl -LO https://github.com/indrasvat/gp-vpn/releases/latest/download/gp-vpn-darwin-arm64.tar.gz
tar -xzf gp-vpn-darwin-arm64.tar.gz
# Remove macOS quarantine attribute (required for unsigned binaries)
xattr -dr com.apple.quarantine gp-vpn
# Move to a directory in your PATH
mkdir -p ~/.local/bin
mv gp-vpn ~/.local/bin/
# Verify installation
gp-vpn versionNote: Since the binary is not signed with an Apple Developer certificate, macOS Gatekeeper will block it by default. The
xattr -dr com.apple.quarantinecommand removes this block. Alternatively, you can right-click the binary in Finder, select "Open", and confirm you want to run it.
Requires Go 1.25+ installed.
# Clone the repository
git clone https://github.com/indrasvat/gp-vpn.git
cd gp-vpn
# Build and install to ~/.local/bin
make build
make install
# Or install to a custom location
make install INSTALL_DIR=/usr/local/binIf ~/.local/bin is not in your PATH, add this to your ~/.zshrc or ~/.bashrc:
export PATH="$HOME/.local/bin:$PATH"Then restart your terminal or run source ~/.zshrc.
# Check connection status
gp-vpn status
# Connect to VPN (uses default portal or currently selected)
gp-vpn connect
# Connect to a specific portal
gp-vpn connect vpn.example.com
# Connect using a shortcut defined in config
gp-vpn connect work
# Connect and wait for connection to establish
gp-vpn connect --wait --timeout 30s
# Switch portals (auto-disconnects and reconnects)
gp-vpn connect backup # Seamlessly switches from current portal
# Disconnect from VPN
gp-vpn disconnect
# Toggle connection (disconnect if connected, connect if disconnected)
gp-vpn toggle
gp-vpn toggle work # Toggle with specific portal
# List configured portal shortcuts
gp-vpn portals
# Discover portals from GlobalProtect Settings
gp-vpn portals discover
# Generate config file from discovered portals
gp-vpn config init
# Generate config to stdout
gp-vpn config init -
# Show current configuration
gp-vpn config show
# Launch interactive TUI
gp-vpn tui
# Show version
gp-vpn versionBy default, gp-vpn outputs machine-parseable text:
$ gp-vpn status
connected:vpn.example.com
$ gp-vpn status
disconnected
$ gp-vpn connect work
success:vpn.example.com
$ gp-vpn connect work # When already connected to work
already_connected:vpn.example.com
$ gp-vpn disconnect
successUse --json for JSON output:
$ gp-vpn status --json
{
"connected": true,
"portal": "vpn.example.com"
}
$ gp-vpn version --json
{
"version": "1.0.0",
"commit": "abc1234",
"build_date": "2025-12-24T12:00:00Z",
"go_version": "go1.25.5",
"os": "darwin",
"arch": "arm64",
"tested_gp_version": "6.3.1"
}Launch the interactive terminal UI with gp-vpn tui:
╭───────────────────────────────────────────────────╮
│ │
│ gp-vpn │
│ │
│ ▄▀▀▀▀▀▀▀▄ │
│ ▄▀ ╱▲╲ ▀▄ │
│ ▐ ▕██▏ ▌ ● Connected │
│ ▐────▕██▏────▌ vpn.example.com │
│ ▐ ▕██▏ ▌ │
│ ▀▄ ╲▼╱ ▄▀ │
│ ▀▄▄▄▄▄▄▄▀ │
│ │
│ 1. ● work vpn.example.com │
│ 2. › backup vpn-backup.example.com │
│ 3. office office.company.org │
│ │
│ d disconnect · 1-3 select · ↑↓ nav · r refresh │
│ │
╰───────────────────────────────────────────────────╯
The TUI features a GlobalProtect-inspired ASCII globe that changes based on connection state:
| State | Globe Color | Description |
|---|---|---|
| Connected | 🟢 Green | Globe with shield overlay |
| Connecting/Switching | 🟡 Yellow | Plain globe (animated) |
| Disconnected | ⚪ Gray | Plain globe |
Keyboard Shortcuts:
Enterorc- Connect to selected portald- Disconnect1-9- Quick-select portal by number↑/↓ork/j- Navigate portal listr- Refresh statusq- Quit
A redesigned terminal interface with card-based layout, 3D status spheres, and network stats. Launch with gp-vpn tui2:
╭──────────────────────────────────────────────────────────────────────────────╮
│ │
│ VPN Status │
│ GlobalProtect Secure │
│ │
│ ▄▄▄▄▄▄▄ │
│ ▄▄▄▄▄▄▄▄▄▄▄ │
│ ▄▄▄▄▄▄▄▄▄▄▄▄▄ │
│ ███████████████ │
│ ███████✓███████ │
│ ███████████████ │
│ ▀▀▀▀▀▀▀▀▀▀▀▀▀ │
│ ▀▀▀▀▀▀▀▀▀▀▀ │
│ ▀▀▀▀▀▀▀ │
│ Connected │
│ ● 00:14:32 │
│ ▲ vpn.example.com │
│ │
│ ╭────────────────────────╮ ╭────────────────────────╮ │
│ │ ASSIGNED IP │ │ EXIT IP │ │
│ │ 10.0.42.85 │ │ 203.0.113.42 (42ms) │ │
│ ╰────────────────────────╯ ╰────────────────────────╯ │
│ │
│ Available Gateways r Refresh │
│ │
│ ╭──────────────────────────────────────────────────────────────────────╮ │
│ │ ▶ 1. work vpn.example.com ● ✓ │ │
│ ╰──────────────────────────────────────────────────────────────────────╯ │
│ ╭──────────────────────────────────────────────────────────────────────╮ │
│ │ 2. backup vpn-backup.example.com ○ │ │
│ ╰──────────────────────────────────────────────────────────────────────╯ │
│ │
│ ╭────────────────╮ ╭──────────────────╮ │
│ │ Disconnect │ │ Switch Gateway │ │
│ ╰────────────────╯ ╰──────────────────╯ │
│ [ d ] [ ↵ ] │
│ │
│ ↑↓ navigate · 1-9 select · q quit │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
The modern TUI features 3D shaded spheres that indicate connection state:
| State | Sphere | Description |
|---|---|---|
| Connected | 🟢 Green 3D sphere | Checkmark in center |
| Connecting/Switching | 🟡 Amber 3D sphere | Animated spinner |
| Disconnected | ⚪ Gray 3D sphere | Muted appearance |
Features:
- Network stats panel showing Assigned IP, Exit IP, and latency
- Card-based gateway list with selection highlighting
- Neon-styled action buttons with keyboard hints
- Real-time uptime counter when connected
- Auto-selects connected portal on status updates
- Flash-free stats fetching (no UI flashes from AppleScript)
Configuration is stored in TOML format. gp-vpn searches for config in this order:
- Path specified with
--configflag GPVPN_CONFIGenvironment variable./gp-vpn.toml(current directory)~/.config/gp-vpn/config.toml~/.gp-vpn.toml
# ~/.config/gp-vpn/config.toml
# Default portal shortcut to use when no portal is specified
default_portal = "work"
# Portal shortcuts - map friendly names to full portal addresses
[portals]
work = "vpn.company.com"
backup = "vpn-backup.company.com"
office = "office.company.org"
# TUI settings
[tui]
refresh_interval = "30s" # Auto-refresh interval (disabled by default, press 'r' to refresh)
theme = "dark" # dark, light, or auto
# Logging settings
[log]
level = "info" # debug, info, warn, error
file = "~/.local/share/gp-vpn/gp-vpn.log" # Optional log fileThe easiest way to set up gp-vpn is to generate a config file from your existing GlobalProtect installation:
# Generate config file (writes to ~/.config/gp-vpn/config.toml)
$ gp-vpn config init
Generated config file: /Users/you/.config/gp-vpn/config.toml
Discovered 3 portal(s):
vpn -> vpn.company.com
vpn-backup -> vpn-backup.company.com
office -> office.company.orgThis opens GlobalProtect Settings, reads configured portals, and generates a TOML config with shortcuts.
To preview without writing a file:
$ gp-vpn config init -
# gp-vpn Configuration
# Generated by: gp-vpn config init
...To see what portals are configured without generating a config:
$ gp-vpn portals discover
Discovered 3 portal(s):
vpn.company.com
vpn-backup.company.com
office.company.org#!/bin/bash
if [[ $(gp-vpn status) == disconnected ]]; then
echo "VPN not connected. Connecting..."
gp-vpn connect --wait
fi
# Run your command that requires VPN
ssh internal-server.company.comAdd to your ~/.zshrc or ~/.bashrc:
# Auto-connect to VPN if not connected
if command -v gp-vpn &> /dev/null; then
if [[ $(gp-vpn status 2>/dev/null) == "disconnected" ]]; then
echo "Connecting to VPN..."
gp-vpn connect work
fi
fi# Show VPN status in tmux status bar
# Add to ~/.tmux.conf:
set -g status-right '#(gp-vpn status 2>/dev/null || echo "vpn:unknown")'- Go 1.25+
- golangci-lint (optional, for linting)
- gotestsum (optional, for pretty test output)
- lefthook (optional, for git hooks)
# Download dependencies
make deps
# Build the binary
make build
# Run all checks (format, vet, lint, test, build)
make ci
# Run tests
make test
# Run tests with coverage
make test-cover
# Format code
make fmt
# Run linter
make lint
# Install development tools
make toolsmake help # Show all available targets
# Build
make build # Build the binary
make build-debug # Build with debug symbols
make install # Install to ~/.local/bin
make install INSTALL_DIR=/usr/local/bin # Custom install path
make uninstall # Remove from install location
make clean # Remove build artifacts
# Development
make dev # Build and run status command
make run ARGS="status" # Run without building
make watch # Watch for changes and rebuild (requires entr)
# Quality
make test # Run tests
make test-cover # Run tests with coverage report
make lint # Run golangci-lint
make fmt # Format code
make fmt-check # Check code formatting
make vet # Run go vet
make staticcheck # Run staticcheck
make check # Run all checks
make ci # Full CI pipeline
# Dependencies
make deps # Download dependencies
make tidy # Tidy go.mod
make verify # Verify dependencies
make update-deps # Update all dependencies
make tools # Install development tools
# Git Hooks
make hooks # Setup lefthook git hooks
# Release
make release # Build release binaries
make version # Show version infoThis happens when downloading pre-built binaries. macOS quarantines files from the internet. Fix it with:
xattr -dr com.apple.quarantine ~/.local/bin/gp-vpnOr right-click the binary in Finder, select Open, and confirm.
Your terminal app needs Accessibility permissions. See Granting Accessibility Permissions.
Important: After granting permissions, you must restart your terminal app.
Ensure GlobalProtect is running. You can start it from Applications or check if it auto-starts on login.
This usually means:
- GlobalProtect needs re-authentication - open the GP app manually to check
- The GP UI has changed in a newer version - please open an issue
The menu bar icon might be hidden or the popup failed to appear. Try:
- Click the GlobalProtect icon manually to ensure it works
- Wait a moment and try again
- Restart GlobalProtect
When using --wait, the default timeout is 15 seconds. For slower connections:
gp-vpn connect --wait --timeout 60s- GlobalProtect: 6.3.1
- macOS: 14.0+ (Sonoma)
- Go: 1.25.5
MIT License - see LICENSE for details.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Run
make cito ensure all checks pass - Submit a pull request
- Cobra - CLI framework
- Bubble Tea - TUI framework
- Bubbles - TUI components (spinner, etc.)
- Lip Gloss - TUI styling
- zerolog - Logging
- go-toml - TOML parsing
- testify - Testing toolkit
This project is not affiliated with, endorsed by, or sponsored by Palo Alto Networks. GlobalProtect is a trademark of Palo Alto Networks, Inc. This is an independent, open-source tool that automates the existing GlobalProtect UI via AppleScript.