Build encrypted mesh networks in minutes, not hours. Generate a shared secret, run wgmesh join on each node, and let DHT discovery wire everything together — NAT traversal, endpoint detection, and route management included.
Setting up WireGuard between two machines is simple. Setting it up between ten is a nightmare of key exchanges, endpoint tracking, and config file juggling. Every time you add or remove a node, every other node's config needs updating.
Existing tools either require a coordination server you have to host and trust (Tailscale, Netmaker), or need manual key distribution (innernet). wgmesh takes a different approach:
- Decentralized mode — nodes discover each other automatically via DHT using a shared secret. No coordination server, no manual config. Just
wgmesh joinand you're in the mesh. - Centralized mode — SSH into your fleet, deploy WireGuard configs, and manage the topology from a single state file. Diff-based updates mean minimal disruption.
Both modes handle NAT traversal, route propagation, and persistence across reboots out of the box.
# Generate a secret (once)
wgmesh init --secret
# On every node — same secret, automatic discovery
wgmesh join --secret "wgmesh://v1/<your-secret>"
# Check status
wgmesh status --secret "wgmesh://v1/<your-secret>"That's it. Nodes find each other via DHT, exchange keys, and build the mesh.
Every node becomes a peer to every other node:
node1 <----> node2
^ ^
| |
v v
node3 <----> node4
Nodes with public IPs are configured as endpoints for other nodes. Nodes behind NAT use persistent keepalive to maintain connections. NAT status is detected automatically by comparing the SSH host with the detected public IP.
Deploying changes reads the current WireGuard state via wg show dump, calculates a diff against the desired state, and applies changes with wg set — no interface restart needed. Routes are managed the same way: stale routes are removed and new ones added in-place.
Mesh state is persisted in /var/lib/wgmesh/. In centralized mode, the state file (mesh-state.json) holds the full topology including keys and node metadata. In decentralized mode, each node stores its keypair in /var/lib/wgmesh/{interface}.json. WireGuard configuration persists across reboots via systemd (wg-quick@wg0.service).
Nodes self-discover and peer automatically via DHT.
# 1) Generate a mesh secret (run once)
wgmesh init --secret
# 2) Join on each node using the same secret
wgmesh join --secret "wgmesh://v1/<your-secret>"
# 3) Check local derived mesh parameters
wgmesh status --secret "wgmesh://v1/<your-secret>"Common join options:
wgmesh join \
--secret "wgmesh://v1/<your-secret>" \
--advertise-routes "192.168.10.0/24,10.0.0.0/8" \
--listen-port 51820 \
--interface wg0 \
--log-level debug \
--gossipManage WireGuard across your fleet from a single control node via SSH:
wgmesh -init # Create mesh state
wgmesh -add node1:10.99.0.1:192.168.1.10 # Add nodes
wgmesh -add node2:10.99.0.2:203.0.113.50
wgmesh -deploy # Push configs via SSHSee docs/centralized-mode.md for the full reference: encrypted state files, custom state paths, routable networks, and vault integration.
Centralized mode supports group-based network segmentation. Assign nodes to groups, define policies that control which groups can communicate, and wgmesh enforces the rules via WireGuard AllowedIPs filtering. Without access control, all nodes form a full mesh.
See docs/access-control.md for the full reference with examples (three-tier architecture, hub-and-spoke, etc.).
Once the daemon is running (decentralized mode), query it for peer information:
# List all active peers
wgmesh peers list
# Show peer counts
wgmesh peers count
# Get specific peer details
wgmesh peers get <pubkey>The RPC socket is automatically created at:
/var/run/wgmesh.sock(if running as root)$XDG_RUNTIME_DIR/wgmesh.sock(if running as non-root)/tmp/wgmesh.sock(fallback)
Override with --socket-path flag on join or WGMESH_SOCKET environment variable.
Use test-peer to verify direct UDP connectivity to another wgmesh node. Start wgmesh join on the remote peer, note its exchange port, then run:
wgmesh test-peer --secret "wgmesh://v1/<your-secret>" --peer <PEER_IP>:<EXCHANGE_PORT>wgmesh exposes a Prometheus-compatible /metrics endpoint. Enable it with the --metrics flag on join:
wgmesh join --secret "wgmesh://v1/<your-secret>" --metrics :9090Then scrape http://<host>:9090/metrics.
| Metric | Type | Description |
|---|---|---|
wgmesh_active_peers |
Gauge | Current active peers in the mesh |
wgmesh_relayed_peers |
Gauge | Peers routed via relay (not direct) |
wgmesh_nat_type{type} |
Gauge | Local NAT type — type is cone, symmetric, or unknown; value is 1 for the current type |
wgmesh_discovery_events_total{layer} |
Counter | Peer-discovery events by layer — layer is dht, lan, gossip, or registry |
wgmesh_nat_traversal_attempts_total{method} |
Counter | NAT traversal attempts by method |
wgmesh_nat_traversal_successes_total{method} |
Counter | Successful NAT traversal exchanges by method |
wgmesh_probe_rtt_seconds{peer_key} |
Histogram | Mesh probe round-trip time per peer (first 8 chars of pubkey) |
wgmesh_reconcile_duration_seconds |
Histogram | Time spent in the reconcile loop |
go_goroutines |
Gauge | Number of active goroutines (Go runtime) |
go_memstats_alloc_bytes |
Gauge | Allocated heap bytes (Go runtime) |
process_resident_memory_bytes |
Gauge | Resident memory (OS process) |
scrape_configs:
- job_name: wgmesh
static_configs:
- targets: ['<node1>:9090', '<node2>:9090']Download pre-built binaries for your platform from the releases page.
Available architectures: Linux amd64, arm64, armv7.
wget https://github.com/atvirokodosprendimai/wgmesh/releases/latest/download/wgmesh-linux-amd64
chmod +x wgmesh-linux-amd64
sudo mv wgmesh-linux-amd64 /usr/local/bin/wgmeshgit clone https://github.com/atvirokodosprendimai/wgmesh.git
cd wgmesh
go build -o wgmeshRequires Go 1.23+ and WireGuard tools (wg command).
docker pull ghcr.io/atvirokodosprendimai/wgmesh:latest
# For full WireGuard functionality
docker run --rm --privileged --network host \
-v $(pwd)/wgmesh-state:/var/lib/wgmesh \
ghcr.io/atvirokodosprendimai/wgmesh:latest join \
--secret "wgmesh://v1/<your-secret>"See DOCKER.md and DOCKER-COMPOSE.md for detailed Docker deployment guides.
For a step-by-step first-mesh walkthrough covering all installation methods, see docs/quickstart.md.
wgmesh version- Centralized mode: Keys stored in
mesh-state.json— use--encryptfor AES-256-GCM encryption. See ENCRYPTION.md. - Decentralized mode: Each node stores its keypair in
/var/lib/wgmesh/{interface}.jsonwith0600permissions. - WireGuard traffic is encrypted end-to-end.
- SSH authentication: The tool tries the SSH agent first (
SSH_AUTH_SOCK), then~/.ssh/id_rsa,~/.ssh/id_ed25519, and~/.ssh/id_ecdsa. - The tool currently uses
InsecureIgnoreHostKeyfor SSH — consider implementing proper host key verification for production.
wgmesh/
├── main.go # CLI entry point (dual-mode dispatch)
├── cmd/
│ ├── chimney/ # Dashboard server with GitHub API proxy
│ └── lighthouse/ # CDN control plane with xDS
├── pkg/
│ ├── crypto/ # Secret-derived keys, envelope encryption
│ ├── daemon/ # Lifecycle, reconciliation, health checks
│ ├── discovery/ # DHT, gossip, LAN/STUN, peer exchange
│ ├── mesh/ # Mesh state, add/remove/list/deploy
│ ├── wireguard/ # Key gen, config parsing, diffing, apply
│ ├── ssh/ # SSH client, remote WireGuard operations
│ ├── rpc/ # Unix socket JSON-RPC server/client
│ ├── privacy/ # Dandelion stem-fluff routing
│ ├── routes/ # Route management
│ ├── ratelimit/ # Rate limiting
│ ├── proxy/ # Proxy utilities
│ └── lighthouse/ # Lighthouse client library
State files (system-level, not in project directory):
/var/lib/wgmesh/
└── mesh-state.json # Mesh state (created on init)
See the Quickstart Guide troubleshooting section for decentralized-mode issues (daemon, peers, NAT, interface errors).
See docs/troubleshooting.md for centralized-mode SSH runbooks (persistence checks, log viewing, configuration rebuilds).
Contributions are welcome! See CONTRIBUTING.md for guidelines.
Found a bug or have an idea? Open an issue — even small improvements are appreciated.
MIT License - see LICENSE file for details