# wire

[![PyPI](https://img.shields.io/pypi/v/meshpop-wire)](https://pypi.org/project/meshpop-wire/)
[![Python](https://img.shields.io/pypi/pyversions/meshpop-wire)](https://pypi.org/project/meshpop-wire/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Self-hosted WireGuard mesh VPN. Like Tailscale, but you own everything.**

```bash
pip install meshpop-wire
```

Pure Python. No external services. No accounts. No cloud dependency.

---

## Why wire?

| | Manual WireGuard | Tailscale / ZeroTier | wire |
|---|---|---|---|
| Add new node | Edit every peer config | One command | One command |
| Coordination | You manage | Their cloud (required) | Your own server |
| Works if vendor down | Yes | ✗ | ✓ |
| Self-hosted | Manual config | ✗ Enterprise only | ✓ Always |
| IP assignment | Manual | Auto | Auto — deterministic |
| AI management | ✗ | ✗ | ✓ via MCP |

---

## How it works

wire has three components:

| Component | Role | Where |
|---|---|---|
| `wire-server` | Peer registry + STUN | One always-on server |
| `wire` | VPN daemon + CLI | Every node |
| `wire-mcp` | MCP server for AI | Machines with Claude |

```
        ┌─────────────────────────────────┐
        │  Coordination Server            │
        │  HTTP :8787 — peer registry     │
        │  UDP  :8788 — STUN              │
        │  Does NOT carry VPN traffic     │
        └──────────┬──────────┬───────────┘
                   │          │
           ┌───────▼──┐   ┌──▼───────┐
           │  Node A  │◄──►│  Node B  │
           │ 10.99.x.x│   │10.99.y.y │
           └──────────┘   └──────────┘
               direct P2P WireGuard tunnel
```

**Deterministic IPs** — each node's VPN IP is derived from its hardware identity (SHA-256 of hostname + MAC address). Same machine always gets the same IP. No DHCP, no conflicts.

**Auto peer sync** — the daemon polls the coordination server every 30 seconds. New nodes are discovered by the whole fleet automatically.

**NAT traversal** — built-in STUN server lets nodes behind home routers find each other without port forwarding.

---

## Quick Start

### Step 1 — Start the coordination server (once)

```bash
pip install meshpop-wire
wire-server
```

As a systemd service:

```ini
[Unit]
Description=wire coordination server
After=network.target

[Service]
ExecStart=wire-server
Restart=always

[Install]
WantedBy=multi-user.target
```

```bash
systemctl enable --now wire-server
```

### Step 2 — Join each node

```bash
# First time — saves config to /etc/wire/config.json
sudo wire up --server http://YOUR_COORD_SERVER:8787 --name myserver

# After first run:
sudo wire up
```

Done. Every existing node discovers the new node within 30 seconds.

---

## CLI Reference

```bash
wire up [--server URL] [--name NAME]   # Join the mesh
wire down                               # Leave the mesh
wire status                             # View all nodes
wire peers                              # List registered nodes
wire ping web1                          # Ping by name or VPN IP
wire diagnose                           # Full diagnostic check
wire install                            # Check WireGuard installation
```

Example `wire status` output:

```
  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
  ○ backup1   10.99.100.1    198.51.100.30   14m ago  OFFLINE
```

---

## WireGuard Installation

```bash
# Debian / Ubuntu
apt install wireguard wireguard-tools

# RHEL / Fedora
dnf install wireguard-tools

# Alpine
apk add wireguard-tools

# macOS
brew install wireguard-tools wireguard-go
```

Run `wire install` to check your platform.

---

## AI Management via MCP

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

> "Which nodes are currently offline?"
> "Ping all nodes and tell me which have high latency"
> "Why can't web1 reach db1?"

### MCP Tools

| Tool | Description |
|---|---|
| `wire_status` | Full mesh view — all nodes, VPN IPs, last seen |
| `wire_up` / `wire_down` | Bring VPN up or down |
| `wire_peers` | List registered peers |
| `wire_ping` | Ping a peer by name or IP |
| `wire_diagnose` | Full diagnostic check |
| `wire_watchdog` | Check handshake ages, stale connections |

---

## Configuration

Written automatically by `wire up`:

```json
{
  "server_url":  "http://COORD_SERVER:8787",
  "node_name":   "web1",
  "vpn_ip":      "10.99.x.x",
  "listen_port": 51820
}
```

| Context | Path |
|---|---|
| Root / system | `/etc/wire/config.json` |
| User | `~/.wire/config.json` |

### Server environment variables

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

---

## MeshPOP Stack

wire is the network layer of the MeshPOP stack:

```
mpop     Fleet orchestration — monitor, manage, automate
vssh     Authenticated transport — remote exec, file transfer
wire     Encrypted mesh VPN — connects all nodes  ← this
```

Each layer is independently installable and usable.

---

## License

MIT — [MeshPOP](https://github.com/meshpop)
