stunmesh-go

command module
v1.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: GPL-2.0 Imports: 15 Imported by: 0

README

stunmesh-go

STUNMESH is a WireGuard helper tool that establishes peer-to-peer connections through NAT — without any self-hosted coordination infrastructure.

It discovers each node's public IP and port via STUN, encrypts the endpoint with a Curve25519 sealed box, and shares it through a pluggable storage backend (Cloudflare DNS, OpenDHT, or your own script). Every node reads its peers' endpoints from the same storage and configures WireGuard accordingly. No rendezvous server, no relay, no VPS in the middle.

Inspired by manuels' wireguard-p2p project.

📖 Full documentation: docs.stunmesh.dev

Talks & Presentations

NAT Type Support

  • Full Cone NAT, Restricted Cone NAT, Port Restricted Cone NAT: fully supported
  • ⚠️ Symmetric NAT: may be difficult to support due to unpredictable port mapping

For best results, ensure at least one peer is behind a cone NAT type.

Supported Platforms

  • Linux (amd64, arm, arm64, mipsle)
  • macOS (amd64, arm64)
  • FreeBSD (amd64, arm64) - requires wireguard-tools
  • Windows (amd64, arm64) - shipped as stunmesh-windows-<arch>-<tag>.zip, requires the official WireGuard for Windows client

[!IMPORTANT] FreeBSD binaries use the wgcli backend, which invokes wg(8). The base system ships the if_wg kernel module but not that tool, so pkg install wireguard-tools is required. See Backend Selection.

[!NOTE] We only support wireguard-go in MacOS, Wireguard App store version is not supported because of sandbox currently.

Quick Start

Download a binary from the releases page, or use the container image:

docker pull tjjh89017/stunmesh

On Linux, macOS, and FreeBSD, stunmesh-go needs raw socket access, so run it as root next to an already-configured WireGuard interface (Windows differs — see Windows below):

sudo ./stunmesh-go

It runs as a daemon by default; pass -oneshot to publish and establish 3 times and then exit.

Configuration is read from /etc/stunmesh/config.yaml, ~/.stunmesh/config.yaml, or ./config.yaml (.yml also works), or pass a file directly with -c <file>. A minimal two-node setup with the built-in Cloudflare plugin:

---
refresh_interval: "1m"
log:
  level: "info"
interfaces:
  wg0:
    peers:
      "PEER_B":
        public_key: "<PEER_B_PUBLIC_KEY_BASE64>"
        plugin: cf
stun:
  addresses: ["stun.l.google.com:19302"]
plugins:
  cf:
    type: builtin
    name: cloudflare
    zone: example.com
    token: "<CLOUDFLARE_API_TOKEN>"
    subdomain: wg

Run the same setup on the other node (with this node's public key), wait roughly two refresh intervals, and the tunnel comes up. Verify with wg show or by pinging the peer's tunnel address.

[!IMPORTANT] stunmesh-go reads the WireGuard device's state once at startup — restart it after the interface is recreated, the listen port or fwmark changes, or config.yaml is edited. Under systemd, bind it to the WireGuard unit (After= + BindsTo=) so this is enforced automatically. See when stunmesh-go must be restarted.

Windows

Windows has no equivalent of the raw sockets (Linux) or pcap (macOS/BSD) stunmesh-go uses to share WireGuard's UDP port, so it instead runs a local UDP proxy that owns the public-facing socket, performs STUN on it, and relays WireGuard packets to per-peer loopback listeners. The official WireGuard for Windows client stays the data plane, and stunmesh-go rewrites each peer's endpoint to its loopback listener itself — there is nothing to change by hand in the tunnel.

  1. Install the official WireGuard for Windows client and create the tunnel.
  2. Activate the tunnel before starting stunmesh-go.
  3. Run stunmesh-go from an Administrator console. The WireGuard service requires the same privilege; without it stunmesh-go fails at the first device access with a "run stunmesh as Administrator" error.
  4. Allow inbound UDP for the executable. The rule must be per-program, not per-port — the public port is ephemeral by design and changes on every restart:
netsh advfirewall firewall add rule name="stunmesh" dir=in action=allow protocol=UDP program="C:\stunmesh\stunmesh.exe" enable=yes

The configuration file is unchanged from the other platforms and the proxy needs no configuration of its own; set interfaces.<name>.proxy.listen only if you need a fixed outer port for port forwarding or a port-based firewall.

[!IMPORTANT] Deactivating and reactivating a tunnel in the WireGuard UI wipes the endpoints stunmesh-go set, because the service re-applies the .conf file. Restart stunmesh-go after any tunnel toggle or restart.

[!NOTE] ICMP ping monitoring is not implemented on Windows yet; stunmesh-go logs this and continues without it.

Documentation

Topic Link
Getting started docs.stunmesh.dev/getting-started
Configuration reference (protocols, STUN servers, ping monitoring) docs.stunmesh.dev/configuration/overview
Storage plugins (built-in, exec, shell, dedup) docs.stunmesh.dev/plugins/overview
Writing your own plugin docs.stunmesh.dev/plugins/exec-protocol
Deployment guides (VyOS, macOS, OSPF/VRF) docs.stunmesh.dev/guides/vyos
Building from source & backend selection docs.stunmesh.dev/reference/build
Platform internals (fwmark, BPF capture, listen interfaces) docs.stunmesh.dev/reference/platform-internals

Build

make all

[!NOTE] For FreeBSD and MacOS, please use GNU Makefile gmake to build.

Build options (built-in plugin selection, binary minimization, WireGuard backend) are documented at docs.stunmesh.dev/reference/build. Contrib plugins live in contrib/ and are built with make plugin.

Future work / Roadmap

  • auto execute when routing engine notify change

License

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
contrib
cloudflare module
internal
ctrl/mock
Package mock_ctrl is a generated GoMock package.
Package mock_ctrl is a generated GoMock package.
entity/mock
Package mock_entity is a generated GoMock package.
Package mock_entity is a generated GoMock package.
plugin/registry
Package registry holds the built-in plugin registry.
Package registry holds the built-in plugin registry.
repo/mock
Package mock_repo is a generated GoMock package.
Package mock_repo is a generated GoMock package.
stun
build +linux
build +linux
wg
wgproxy
Package wgproxy is the UDP proxy fronting a WireGuard interface on platforms without raw-socket port sharing (Windows).
Package wgproxy is the UDP proxy fronting a WireGuard interface on platforms without raw-socket port sharing (Windows).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL