feat(vpn): Proton VPN Hermes skill — CLI wrapper tools

Builds the proton-vpn skill per ARCHITECTURE.md section 6 with 9 tools:

Tools:
- proton_vpn_connect — connect with fastest/random/country/city/P2P/Tor/SC selection
- proton_vpn_disconnect — disconnect current session
- proton_vpn_status — check connection status (parse CLI output)
- proton_vpn_servers — list servers with filters (country, features)
- proton_vpn_killswitch — enable/disable kill switch
- proton_vpn_config — view/modify DNS, NetShield, protocol
- proton_vpn_login — initiate browser OAuth login
- proton_vpn_logout — clear credentials
- proton_vpn_refresh — refresh server list and config

Implementation:
- Python subprocess wrapper around official protonvpn-cli v1.0+
- Human-readable CLI output parsed into structured JSON
- Privilege check (protonvpn group) before privileged operations
- 30-60s timeouts with graceful error handling
- dispatch() entry point for Hermes tool routing

Also includes:
- scripts/install.sh — distro-aware dependency installer
- references/commands.md — CLI quick reference
- .gitignore — exclude __pycache__, env, debug files

Deviations from ARCHITECTURE.md noted in docs:
- CLI uses 'login' (browser OAuth), not 'init'
- No --json output — parsed from tables
- Install via Proton repos, not PyPI
This commit is contained in:
Templeton Peck 2026-06-08 18:29:53 +02:00
parent 8fdf219337
commit da7dac8301
Signed by: face
GPG key ID: 8696A18EFB764ADE
6 changed files with 1454 additions and 0 deletions

406
skills/proton-vpn/SKILL.md Normal file
View file

@ -0,0 +1,406 @@
---
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 <ip>`
**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)