--- name: proton-vpn description: Proton VPN management — connect, disconnect, status, server list, kill switch, and config via the official protonvpn-cli version: 1.0.0 category: infra platforms: [linux] dependencies: - protonvpn-cli (official Proton VPN Linux CLI) - systemd-resolved (for DNS leak protection) - gnome-keyring (for credential storage) - NetworkManager (for connection management) metadata: hermes: tags: [vpn, proton, network, privacy, security] auth: independent — protonvpn-cli manages its own OAuth session limitations: - "Does NOT work on headless setups (requires gnome-keyring + NetworkManager)" - "Cannot run alongside Proton VPN GUI app" - "Split tunneling not yet available in CLI v1.0.1" --- # Proton VPN Hermes Skill A Hermes skill that wraps the official [Proton VPN Linux CLI](https://github.com/ProtonVPN/proton-vpn-cli) (`protonvpn-cli`) for agent use. Provides full VPN lifecycle management — connect, disconnect, status, server discovery, kill switch, and configuration. ## Installation ### Prerequisites The skill shells out to `protonvpn-cli` v1.0.0+. Install it via the official Proton repositories: ```bash # Debian/Ubuntu (add Proton repo first) curl -1sLf 'https://repo.protonvpn.com/debian/dists/stable/main/signed.key' | sudo apt-key add - sudo add-apt-repository 'deb https://repo.protonvpn.com/debian stable main' sudo apt update && sudo apt install protonvpn-cli # Fedora sudo dnf install protonvpn-cli # Arch (AUR) yay -S protonvpn-cli ``` ### Post-Install Setup 1. **Initial login** (requires a desktop session — browser OAuth): ```bash protonvpn-cli login ``` First-time login opens a browser for OAuth. After that, the session persists. 2. **Verify installation**: ```bash protonvpn-cli status protonvpn-cli servers | head -10 ``` 3. **Ensure required services**: ```bash systemctl status systemd-resolved # should be running ``` ## Tools ### `proton_vpn_connect` Connect to a Proton VPN server. ```json { "name": "proton_vpn_connect", "description": "Connect to a Proton VPN server. Supports fastest, random, country, city, P2P, Tor, Secure Core, and free server selection.", "parameters": { "type": "object", "properties": { "server": { "type": "string", "description": "Server name or ID (e.g., 'US-NY#1'). Omitting uses default selection strategy." }, "selection": { "type": "string", "enum": ["fastest", "random", "country", "city", "p2p", "tor", "free", "secure-core"], "description": "Server selection strategy. 'fastest' (default): lowest latency. 'random': random server. 'country': fastest in specified country. 'p2p'/'tor'/'secure-core': feature-optimized." }, "country": { "type": "string", "description": "Two-letter country code (e.g., 'US', 'CH', 'JP'). Only used when selection='country'." }, "city": { "type": "string", "description": "City name (e.g., 'New York'). Only used when selection='city'." }, "protocol": { "type": "string", "enum": ["wireguard", "openvpn_udp", "openvpn_tcp"], "description": "VPN protocol. Default is WireGuard when available." }, "persistent": { "type": "boolean", "description": "Auto-reconnect if the VPN connection drops." } } } } ``` **CLI mapping:** - Fastest: `protonvpn-cli connect --fastest` - Random: `protonvpn-cli connect --random` - Country: `protonvpn-cli connect --country US --protocol wireguard` - P2P: `protonvpn-cli connect --p2p` - Server name: `protonvpn-cli connect US-NY#1` **Returns:** JSON with connection status, server name, protocol, and IP. --- ### `proton_vpn_disconnect` Disconnect the current VPN session. ```json { "name": "proton_vpn_disconnect", "description": "Disconnect the current VPN session and restore normal network connectivity.", "parameters": {} } ``` **CLI mapping:** `protonvpn-cli disconnect` **Returns:** JSON with confirmation message. --- ### `proton_vpn_status` Get current VPN connection status. ```json { "name": "proton_vpn_status", "description": "Check the current Proton VPN connection status — connected server, protocol, uptime, IP information.", "parameters": {} } ``` **CLI mapping:** `protonvpn-cli status` **Returns:** JSON with connection state, server name, country, protocol, uptime, and local IP. --- ### `proton_vpn_servers` List available Proton VPN servers with features and load. ```json { "name": "proton_vpn_servers", "description": "List available Proton VPN servers. Shows country, city, current load percentage, and supported features (P2P, Tor, Secure Core).", "parameters": { "type": "object", "properties": { "country": { "type": "string", "description": "Filter by two-letter country code (e.g., 'US')." }, "features": { "type": "array", "items": { "type": "string", "enum": ["p2p", "tor", "secure-core", "free"] }, "description": "Filter by required features." }, "format": { "type": "string", "enum": ["table", "json"], "description": "Output format. 'table': human-readable. 'json': machine-parseable." } } } } ``` **CLI mapping:** `protonvpn-cli servers` (table output, parsed to JSON) **Returns:** JSON array of servers with name, country, city, load, features. --- ### `proton_vpn_killswitch` Enable or disable the VPN kill switch. ```json { "name": "proton_vpn_killswitch", "description": "Enable or disable the VPN kill switch. When enabled, all internet traffic is blocked outside the VPN tunnel, preventing data leaks if the VPN drops.", "parameters": { "type": "object", "properties": { "state": { "type": "string", "enum": ["on", "off"], "description": "Kill switch state. 'on': enable (blocks non-VPN traffic). 'off': disable." } }, "required": ["state"] } } ``` **CLI mapping:** `protonvpn-cli settings --killswitch on|off` **Returns:** JSON with kill switch state and confirmation. --- ### `proton_vpn_config` View or modify VPN configuration. ```json { "name": "proton_vpn_config", "description": "View current Proton VPN configuration or modify settings like DNS, NetShield, protocol preference, and VPN Accelerator.", "parameters": { "type": "object", "properties": { "action": { "type": "string", "enum": ["view", "set-dns", "set-netshield", "set-protocol"], "description": "'view' (default): show current config. 'set-dns': set custom DNS servers. 'set-netshield': toggle NetShield ad-blocker. 'set-protocol': set preferred protocol." }, "value": { "type": "string", "description": "Value for the setting. For 'set-dns': comma-separated IPs (e.g., '1.1.1.1,1.0.0.1'). For 'set-netshield': 'on', 'off', or 'strict'. For 'set-protocol': 'wireguard', 'openvpn_udp', or 'openvpn_tcp'." } } } } ``` **CLI mapping:** - View: `protonvpn-cli config --list` - Kill switch: `protonvpn-cli settings --killswitch on|off` - NetShield: `protonvpn-cli settings --netshield on|off|strict` - DNS: `protonvpn-cli settings --custom-dns ` **Returns:** JSON with configuration key-value pairs or confirmation message. --- ### `proton_vpn_login` Authenticate with Proton VPN (browser OAuth). ```json { "name": "proton_vpn_login", "description": "Authenticate with Proton VPN. Requires a desktop session to open a browser for OAuth login. Proton VPN CLI manages credentials after initial login.", "parameters": { "type": "object", "properties": { "username": { "type": "string", "description": "Proton account username/email." } } } } ``` **CLI mapping:** `protonvpn-cli login [username]` **Note:** First-time login requires a browser for OAuth. The agent should warn users that this won't work in headless environments. --- ### `proton_vpn_logout` Log out from Proton VPN. ```json { "name": "proton_vpn_logout", "description": "Log out from Proton VPN. Clears stored credentials and disconnects if connected.", "parameters": {} } ``` **CLI mapping:** `protonvpn-cli logout` --- ### `proton_vpn_refresh` Refresh server list and VPN configuration. ```json { "name": "proton_vpn_refresh", "description": "Refresh the server list and VPN configuration from Proton's API. Useful after a network change or if servers appear outdated.", "parameters": {} } ``` **CLI mapping:** `protonvpn-cli refresh` --- ## Implementation All tools shell out to `protonvpn-cli` via Python `subprocess`. The implementation module lives at `scripts/tools.py` in this skill directory. ### General Pattern ```python import subprocess import json import shlex def _run_vpn_command(args: list[str], timeout: int = 30) -> dict: """Run a protonvpn-cli command and return structured output.""" try: result = subprocess.run( ["protonvpn-cli"] + args, capture_output=True, text=True, timeout=timeout ) if result.returncode != 0: return {"error": result.stderr.strip(), "exit_code": result.returncode} return { "success": True, "output": result.stdout.strip(), "command": "protonvpn-cli " + " ".join(shlex.quote(a) for a in args) } except subprocess.TimeoutExpired: return {"error": f"Command timed out after {timeout}s"} except FileNotFoundError: return {"error": "protonvpn-cli not found. Install via Proton repos."} ``` ### Privilege Check `protonvpn-cli` requires either root or membership in the `protonvpn` group for WireGuard interface creation. All tools should call a privilege check before executing: ```python import os def _check_vpn_privileges() -> dict | None: """Check if the current user can use protonvpn-cli. Returns error dict or None.""" if os.geteuid() == 0: return None # root can always use it import subprocess groups_result = subprocess.run(["groups"], capture_output=True, text=True) groups = groups_result.stdout.strip() if "protonvpn" not in groups: return { "error": ( "User not in 'protonvpn' group. " "Run: sudo usermod -aG protonvpn $USER && logout && login" ) } return None ``` ### Output Parsing The CLI outputs human-readable tables (not JSON). The tool parses output into structured JSON: - `protonvpn-cli status` → parsed into connection state, server, protocol, uptime, IP - `protonvpn-cli servers` → parsed into array of server objects - `protonvpn-cli config --list` → parsed into key-value pairs ## Dependencies | Dependency | Required | Notes | |------------|----------|-------| | `protonvpn-cli` (>=1.0.0) | Yes | Official Proton VPN CLI, v1.0.1 latest (Apr 2026). [GitHub](https://github.com/ProtonVPN/proton-vpn-cli) | | `systemd-resolved` | Recommended | DNS leak protection via systemd-resolved | | `gnome-keyring` | Recommended | Credential storage for initial login | | `NetworkManager` | Required | VPN connection profiles managed via NetworkManager | | `WireGuard` | Recommended | Default protocol; faster than OpenVPN | | `OpenVPN` | Fallback | Alternative protocol when WireGuard unavailable | ## Limitations 1. **No headless support** — The CLI requires `gnome-keyring` and `NetworkManager`. It does not work on minimal server installs without a desktop environment. 2. **Cannot coexist with Proton VPN GUI** — Running both simultaneously causes conflicts. 3. **Split tunneling not yet available** — Feature is planned for a future release. 4. **Requires internet for login** — OAuth flow needs a browser. 5. **Kill switch uses iptables** — May conflict with other firewall rules. Verify carefully. ## Security - Proton VPN CLI stores credentials in `gnome-keyring` (encrypted at rest) - Connection logs: `~/.cache/Proton/VPN/logs/` - Configuration: `~/.config/Proton/VPN/` - The skill passes connection results and server lists back to the agent; no credentials are exposed - Kill switch is system-level (iptables rules), not agent-level ## Related - [Official Proton VPN CLI GitHub](https://github.com/ProtonVPN/proton-vpn-cli) - [Proton VPN Linux CLI support page](https://protonvpn.com/support/linux-cli) - [Hermes hardware keychain skill](https://github.com/NousResearch/hermes-agent) (example of subprocess skill pattern) - [ARCHITECTURE.md](../../ARCHITECTURE.md) — Hermes-Proton integration design (section 6)