A VPN-whitelist guard for Claude on macOS.
Blocks the Claude Desktop app, the Claude Code CLI (claude) and every Claude/Anthropic domain (claude.ai, claude.com, anthropic.com and all their subdomains) whenever your public IP is not on your VPN whitelist - so you never touch Claude from the wrong region by accident.
English · Русский
If your account is region-sensitive, a single request from the wrong IP can get your account flagged. ClaudeGuard solves that: every launch and every running session is checked against your whitelist of allowed IPs, and access is cut the instant you fall off it.
- 🔐 IP whitelist - Claude opens only when your public IP matches one of your VPN nodes.
- ⏱ Instant pre-flight check - launching
claudeorClaude.appverifies the public IP in < 0.5s (STUN over UDP). Not whitelisted → blocked before anything connects. - 🧠 One brain, one verdict - a background daemon publishes the decision to a state file; the CLI, the app launcher and the menu bar all read the same verdict, so nothing can disagree.
- 🚀 Autostart at login via
LaunchAgent. - 🟢 Menu bar UI - live status (🟢 protected / 🔴 blocked / ⚪ offline / 🟡 off), toggles, one-click whitelist.
- ❄️ Freeze auto-updates - blocks Claude's update servers and locks the update dirs.
- 🤖 Default model override - pin a model for every
claudesession (e.g.claude-opus-4-8) from the menu bar; the CLI wrapper injects--modelautomatically so you don't have to type it each time.
The installer compiles the native binaries (Swift + C) on your machine. Locally-compiled binaries carry no quarantine flag, so Gatekeeper never blocks them - no Apple Developer account, no code-signing, no notarization, no Homebrew. The only requirement is Xcode Command Line Tools (the installer offers to install them if missing).
Two ways to install - both give the exact same result.
Option 1 - one line (downloads and builds from GitHub):
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ivblz/ClaudeGuard/main/install.sh)"Option 2 - from a local copy (a cloned or downloaded repo; never touches GitHub):
./install.shEither way it compiles for your architecture (Apple Silicon or Intel), installs the menu bar daemon at login,
and wraps the claude command. It asks for your password once (sudo is needed to edit /etc/hosts and pf)
and offers to whitelist your current IP - accept only if you are on your VPN right now.
Uninstall:
./uninstall.shAfter install the claudeguard command is available:
| Command | What it does |
|---|---|
claudeguard status |
Current status, IP and whitelist |
claudeguard add-ip <ip> |
Add an IP or CIDR to the whitelist |
claudeguard remove-ip <ip> |
Remove an IP from the whitelist |
claudeguard list-ips |
List whitelisted IPs |
claudeguard enable-protection / disable-protection |
Turn protection on / off |
claudeguard block-updates / allow-updates |
Lock / unlock auto-updates |
claudeguard set-model <model> |
Set the default model (e.g. claude-opus-4-8) |
claudeguard enable-model / disable-model |
Turn the model override on / off |
claudeguard launch-desktop |
Launch Claude Desktop with a pre-flight check |
claudeguard start / stop |
Start / stop the menu bar daemon |
claudeguard doctor |
Check every hook is still attached, and re-attach it |
claudeguard set-cli-path <path> |
Point at the real claude binary if auto-detect misses it |
Reinstalling Claude Code puts the real claude binary back over ClaudeGuard's shim,
and updating it deletes the version-stamped path the shim hands off to - so the guard
would silently detach while the menu bar still showed 🟢. The same applies to Claude
Desktop, whose bundle id and update directories have been renamed before.
ClaudeGuard therefore discovers those locations instead of hard-coding them, and
re-attaches whatever came loose: the daemon self-heals every 60s, the claude
wrapper re-hooks on each run, and /etc/hosts is re-applied whenever something
external edits it. Run claudeguard doctor to see the state of every hook (and fix
it immediately) at any time.
- STUN-first IP detection - the public IP is resolved via a single UDP STUN round-trip (tens of ms), falling back to HTTP echo services on networks that block UDP.
- The brain (
src/brain.py) turns that IP into one verdict -allowed/blocked/offline- and it fails closed: only a confirmedallowedlets Claude run. - Single source of truth - the menu bar daemon writes its verdict to
~/.config/claudeguard/state.jsonon every check. The CLI wrapper and app launcher read it (instant), and fall back to their own check only if the daemon is down. - Enforcement - the whole Claude/Anthropic domain family (~175 hosts across 5 apex domains) is blocked via
/etc/hosts+ a pf firewall rule (TCP + UDP/QUIC); the desktop app is force-quit with an alert; a runningclaudesession is killed the moment the IP leaves the whitelist.
Scope: this is accidental-leak protection for your own machine, not a defense against a malicious admin. The truly unbypassable layer (a Network Extension content filter) requires a paid Apple Developer account; the on-device approach here is the free ceiling and covers accidental leaks well.
src/
brain.py The single decision core (reads the daemon verdict; STUN fallback)
ip_checker.py Public-IP detection: STUN over UDP, then HTTP
config.py Config at ~/.config/claudeguard/config.json
network_guard.py /etc/hosts + pf firewall block/unblock
update_guard.py Freeze/unfreeze Claude auto-updates
integrity.py Finds Claude's real paths; re-attaches hooks after a reinstall
cli_wrapper.py Pre-flight interceptor for the `claude` command
app_launcher.py Pre-flight interceptor for Claude.app
main.swift Native menu bar daemon (AppKit): monitoring, alerts, state file
hosts_helper.c Root helper run by a LaunchDaemon (no sudoers, no setuid); writes /etc/hosts + pf
bin/claudeguard Management CLI
assets/AppIcon.png App icon
install.sh Compiles natively on-device and installs everything
uninstall.sh Clean removal, restores the original `claude`
MIT - see LICENSE.