Skip to main content

Autonomous WireGuard mesh VPN with P2P, relay fallback, and MCP server

Project description

wire

Self-hosted WireGuard mesh VPN — like Tailscale, but you own everything.

pip install meshpop-wire

No external services. No accounts. No cloud dependency. Pure Python stdlib — installs and runs on any Linux or macOS machine.


Why wire?

The problem with existing VPN solutions

Traditional WireGuard is powerful but painful to manage at scale:

# Old way: manual WireGuard setup for every new node
# On each existing node — add the new peer manually:
wg set wg0 peer <NEW_PUBLIC_KEY> allowed-ips 10.0.0.5/32 endpoint NEW_IP:51820
# Repeat on EVERY other node
# When a node's IP changes — update EVERY other node again
# No central view of who's online

Tailscale / ZeroTier solve this — but you're dependent on their coordination servers. Your VPN stops working if their service goes down. Your traffic metadata goes through their infrastructure.

wire gives you the Tailscale experience with zero external dependencies:

Manual WireGuard Tailscale / ZeroTier wire
Setup per new node Edit every existing node One command One command
Coordination server You manage manually Their cloud (required) Your own server
IP changes Re-configure everything Auto Auto
Works if vendor is down Depends ✗ No ✓ Yes
Self-hosted Manual config files ✗ Enterprise only ✓ Always
Dependencies None Account + agent None
AI management (MCP)

Before wire

# Adding a new server to existing 10-node WireGuard mesh:
# 1. Generate keys on new server
wg genkey | tee private.key | wg pubkey > public.key

# 2. On EVERY existing node — add the new peer
for host in server1 server2 server3 server4 server5 \
            server6 server7 server8 server9 server10; do
  ssh root@$host "wg set wg0 peer <PUBKEY> \
    allowed-ips 10.0.0.11/32 endpoint NEW_SERVER_IP:51820"
done

# 3. Add all existing peers to new server — one by one
# 4. When any server changes its IP — repeat

# Result: 10 servers × 10 config files = manually maintained mesh

After wire

# On new server — one command, done:
sudo wire up --server http://YOUR_COORD_SERVER:8787 --name newserver

# Every existing node automatically discovers and connects to newserver
# within 30 seconds. No manual config on existing nodes.

Architecture

                ┌────────────────────────────────────────┐
                │  Coordination Server  (one always-on)  │
                │  wire_server.py                        │
                │  HTTP :8787  —  peer registry          │
                │  UDP  :8788  —  STUN (NAT discovery)   │
                │                                        │
                │  Knows: who exists, their public IPs   │
                │  Does NOT carry VPN traffic            │
                └──────────┬─────────────┬──────────────┘
                           │             │
               registers / │             │ registers /
               heartbeat   │             │ heartbeat
               every 30s   │             │ every 30s
                           │             │
              ┌────────────▼──┐       ┌──▼────────────┐
              │  Node A       │◄─────►│  Node B        │
              │  wire_client  │       │  wire_client   │
              │  10.99.x.x    │       │  10.99.y.y     │
              └───────────────┘       └───────────────┘
                      direct P2P WireGuard tunnel
                      end-to-end encrypted
                      coordination server not involved

Three files, three roles:

File Role Where it runs
wire_server.py Coordination server + STUN One always-on server
wire_client.py VPN daemon + CLI Every node
wire_mcp_server.py MCP tools for AI agents Machines with Claude

Installation

No external dependencies

wire is pure Python standard library. No pip packages, no system packages beyond Python 3.8+.

pip install meshpop-wire

This installs:

  • wire — CLI command
  • wire-mcp — MCP server for AI integration

WireGuard kernel module (every node)

wire uses the WireGuard Linux kernel module for the actual VPN tunnels. Install the userspace tools:

# Debian / Ubuntu
apt install wireguard wireguard-tools

# RHEL / Fedora / CentOS
dnf install wireguard-tools

# Alpine
apk add wireguard-tools

# macOS
brew install wireguard-tools wireguard-go

Run wire install to check your platform and see the right command.

Step 1 — Start the coordination server (once, on one always-on machine)

# Copy to your server and run
scp wire_server.py user@COORD_SERVER:/opt/wire/
ssh user@COORD_SERVER "python3 /opt/wire/wire_server.py"

As a systemd service:

# /etc/systemd/system/wire-server.service
[Unit]
Description=wire coordination server
After=network.target

[Service]
ExecStart=/usr/bin/python3 /opt/wire/wire_server.py
Restart=always

[Install]
WantedBy=multi-user.target
systemctl enable --now wire-server

Step 2 — Join every node to the mesh

# First time on each node (saves config for future starts):
sudo wire up --server http://COORD_SERVER:8787 --name myserver

# After first run — just:
sudo wire up

That's it. The node generates its own WireGuard key pair, discovers its NAT-mapped port via the built-in STUN server, registers with the coordination server, and starts syncing peers every 30 seconds.


How It Works

VPN IP assignment — no DHCP, no conflicts

Each node's VPN IP is derived deterministically from its own identity:

node_id = sha256(hostname + mac_address)
vpn_ip  = f"10.99.{node_id[0]}.{node_id[1]}"   # e.g. 10.99.23.187

Same machine → same VPN IP every time. No central IP allocation. No conflicts.

NAT traversal — works behind home routers

wire has its own STUN server built in (no Google STUN or external service). Before starting WireGuard, each node probes the coordination server's UDP port to discover what IP and port its NAT router is using:

Node UDP :51820  →  COORD_SERVER UDP :8788
Server sees: EXTERNAL_IP:MAPPED_PORT
Server replies: {"ip": "EXTERNAL_IP", "port": MAPPED_PORT}

Two nodes behind different NAT routers use WireGuard's PersistentKeepalive=25 to punch holes in both NATs simultaneously — tunnels work without any port forwarding.

Peer sync — automatic, every 30 seconds

The client daemon queries /peers from the coordination server every 30 seconds and updates WireGuard with any new or changed peers. When you add a new node, all existing nodes discover it within 30 seconds without any manual action.


CLI Reference

wire up — join the network

# First time (saves config):
sudo wire up --server http://COORD_SERVER:8787 --name NODENAME

# After first run:
sudo wire up
Flag Description
--server / -s Coordination server URL
--name / -n This node's name (default: hostname)
--port / -p WireGuard listen port (default: 51820)

wire status — view the whole mesh

wire status
wire status  http://COORD_SERVER:8787
  4 online / 1 offline / 5 total

  ● web1        10.99.23.187     203.0.113.10       5s ago
  ● web2        10.99.45.22      198.51.100.20      12s ago  (this node)
  ● db1         10.99.87.3       192.0.2.30         8s ago
  ● storage1    10.99.200.5      203.0.113.40       3s ago
  ○ backup1     10.99.100.1      198.51.100.30      14m ago  OFFLINE
wire status --json   # machine-readable

wire peers — list all registered nodes

wire peers

wire ping — ping a node by name

wire ping web1
wire ping 10.99.23.187

Resolves names via the coordination server, then pings over the VPN.


wire down — leave the network

sudo wire down

Removes the WireGuard interface and stops the background daemon. The node appears offline on wire status within 5 minutes.


wire install — check WireGuard installation

wire install

Checks if WireGuard is installed and shows platform-specific instructions if not.


AI Management via MCP

wire ships with an MCP server that lets AI agents (Claude, etc.) manage your VPN mesh through natural language — from initial setup through ongoing monitoring.

Setup

pip install meshpop-wire   # installs wire-mcp automatically

Add to Claude config (~/.claude/settings.json):

{
  "mcpServers": {
    "wire": { "command": "wire-mcp" }
  }
}

What the AI can do

Once connected, you can ask Claude in plain language:

"Set up wire on this server and connect it to the mesh" "Which nodes are currently offline?" "Why can't web1 reach db1?" "Ping all nodes and tell me which have high latency" "Is the WireGuard module installed on this machine?"

The AI handles the commands, interprets the output, and explains what's happening.

MCP Tools Reference

Tool Description
wire_status Full mesh view — all nodes, online/offline, VPN IPs, last seen
wire_up Bring up the VPN tunnel on this machine
wire_down Tear down the VPN tunnel
wire_peers List all registered peers
wire_ping Ping a peer by name or VPN IP
wire_install Check WireGuard installation status
wire_diagnose Full diagnostic: module loaded? interface up? server reachable? peers synced?
wire_watchdog Check peer handshake ages, stale connections, service health

Configuration

Config is written automatically by wire up. Locations:

Context Path
Root / system daemon /etc/wire/config.json
Regular user ~/.wire/config.json

Example:

{
  "server_url":   "http://COORD_SERVER:8787",
  "node_name":    "web1",
  "node_id":      "a1b2c3d4...",
  "vpn_ip":       "10.99.23.187",
  "listen_port":  51820,
  "nat_port":     54321
}

Server-side environment variables:

Variable Default Description
WIRE_PORT 8787 HTTP port (UDP STUN = this + 1)
WIRE_VPN_SUBNET 10.99 VPN IP prefix
WIRE_STATE_FILE /etc/wire/state.json Peer state file

File Reference

wire/
├── wire_server.py      Coordination server + UDP STUN
│                       Run on ONE always-on server
├── wire_client.py      VPN daemon + CLI
│                       Run on EVERY node in the mesh
└── wire_mcp_server.py  MCP server for AI integration

/etc/wire/  (root) or  ~/.wire/  (user)
├── config.json         Node config — written by wire up
├── private.key         WireGuard private key (chmod 600)
├── public.key          WireGuard public key
└── state.json          Server peer state (coordination server only)

Stack Position

wire is Layer 1 of the MeshPOP infrastructure stack:

Layer 3  mpop    Fleet orchestration — monitor, manage, automate
Layer 2  vssh    Fast authenticated transport — remote exec, file transfer
Layer 1  wire    Encrypted mesh VPN — connects all nodes

Each layer is independently installable. wire works without vssh or mpop. vssh works with any VPN or direct network. mpop works with any transport.


Design Principles

No external services. The coordination server you run provides everything including STUN for NAT discovery. Nothing phones home.

No central traffic bottleneck. The coordination server handles only small JSON heartbeats. All VPN traffic flows directly between nodes, peer to peer.

No manual peer management. Add a node once. Every other node discovers it automatically within 30 seconds.

Deterministic IPs. Each machine's VPN IP is derived from its own hardware identity. No DHCP, no allocation table, no conflicts.

Offline tolerance. Nodes keep their WireGuard peers configured when the coordination server is unreachable. Established tunnels survive coordination server restarts.


License

MIT — MeshPOP

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

meshpop_wire-2.4.2.tar.gz (31.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

meshpop_wire-2.4.2-py3-none-any.whl (33.1 kB view details)

Uploaded Python 3

File details

Details for the file meshpop_wire-2.4.2.tar.gz.

File metadata

  • Download URL: meshpop_wire-2.4.2.tar.gz
  • Upload date:
  • Size: 31.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for meshpop_wire-2.4.2.tar.gz
Algorithm Hash digest
SHA256 95eb534c8041212a03b1cf7342fc01dd4b20868f8bdc877c22328b07e87b126f
MD5 3ab71e4a5a3f4ae2f39d1063e052670d
BLAKE2b-256 337cb3517038c57bc13d9d3ef4271e5d891b33b1a91a8925a672aae30519769c

See more details on using hashes here.

File details

Details for the file meshpop_wire-2.4.2-py3-none-any.whl.

File metadata

  • Download URL: meshpop_wire-2.4.2-py3-none-any.whl
  • Upload date:
  • Size: 33.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for meshpop_wire-2.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6db1e7c451f57b514b6b0ca764655d990742effe4fd5481404e9f6944aa39ef0
MD5 6f23a92de2e21880baba89928d5a7750
BLAKE2b-256 549ffc62127b093ab959406f599896c573e37f7ff55d4402b83fef89f372c2f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page