This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
VPN-in-Docker: a Docker Compose setup that routes container traffic through a WireGuard VPN tunnel, secured by iptables firewall rules that block all non-VPN traffic. Uses AirVPN as the default provider but supports any WireGuard-compatible VPN.
docker compose build # Build custom containers
docker compose up # Start all services
docker compose down --volumes --remove-orphans # Full cleanupNo tests, linters, or CI exist. The project is entirely shell scripts and Docker configuration.
Most containers share a single network namespace via network_mode: service:network — this ensures iptables rules apply uniformly. Exceptions: rulemaker uses the default bridge network (so its DNS resolution isn't blocked by the firewall it generates), and webview has its own network (no need to be firewalled).
- network — Sleeps forever; exists solely as a shared network namespace. All ports are exposed here (not on individual containers).
- rulemaker — Resolves VPN server IPs via DNS, generates iptables rules, writes dump files to
cache/iptables/. - firewall — Polls for iptables dump files and atomically applies them via
iptables-restorein the shared network namespace. - wireguard — LinuxServer.io WireGuard image; creates
wg*interfaces in the shared namespace. - status — Periodically checks connectivity (IP detection, traceroute, country lookup via ipstack.com API) and writes ANSI/HTML/text reports.
- webview — Nginx serving the HTML status page.
- transmission — Sample app. Uses
wait-for-safety.shentrypoint to block startup until firewall DROP policies are active.
generate-firewall.sh— Builds iptables rules: DROP-all default, allow loopback/VPN interfaces/local IPs/DNS/VPN server IPs. Runs in rulemaker container, dumps rules viaiptables-save.apply-firewall.sh— Watches for updated dump files, applies them atomically withiptables-restore. Runs in firewall container.update-airvpn-ips.sh— Resolves AirVPN server hostnames (viadig) across multiple scopes (earth, europe, specific countries). Writes IP lists tocache/servers/.wait-for-safety.sh— Entrypoint wrapper that pollsiptables -Luntil INPUT/OUTPUT policies are DROP before executing the wrapped command.report-status.sh— Detects current IP, country (via ipstack.com), next-hop routing, and per-interface connectivity. Outputs colorized ANSI viatoilet.transmission.sh— Launchestransmission-daemonwith config flags.
update-airvpn-ips.shresolves VPN IPs → writes tocache/servers/generate-firewall.shreads VPN IPs + config → generates iptables rules → dumps tocache/iptables/apply-firewall.shdetects new dump files → applies atomically viaiptables-restorewait-for-safety.shdetects DROP policy → starts wrapped application
- VPN network subnet:
172.30.172.0/24(IPv4),fdaa:bbbb:cccc:dddd::/64(IPv6) - DNS:
8.8.4.4/8.8.8.8 - Status check IPs:
139.130.4.5(v4),2620:fe::fe(v6) - Firewall strategy: DROP by default, with explicit ACCEPT for VPN interfaces (
wg+,tun+), local subnets, DNS, and VPN server IPs. Uses DROP (not REJECT) for blocked traffic so apps retry rather than close connections during VPN reconnects.
wireguard/wg0.conf— WireGuard config (not committed)ipstack.env— IPStack API key for country detection (not committed; seeipstack.env.example)- Subnets and allowed IPs are configured via environment variables in
docker-compose.yaml