CLI Commands
meshguard keygen
Generate a new Ed25519 identity keypair.
meshguard keygen [--force]| Flag | Description |
|---|---|
--force | Overwrite existing keys (destructive) |
Output files (in $MESHGUARD_CONFIG_DIR, or the platform default config directory):
identity.key— secret key (permissions0600)identity.pub— public key
Safety: Refuses to overwrite existing keys unless --force is passed.
meshguard export
Print your public key to stdout.
meshguard export > my-node.pubmeshguard trust
Add a peer's public key to your authorized keys.
meshguard trust <key-or-path> [--name <name>]| Argument | Description |
|---|---|
<key-or-path> | Base64 public key string or path to a .pub file |
--name | Human-readable name (default: auto-generated from key) |
Validation:
- Base64 decode check
- Ed25519 point-on-curve check
- Key collision check (same key, different name)
- Name collision check (same name, different key)
meshguard revoke
Remove a peer from your authorized keys.
meshguard revoke <name>Deletes $MESHGUARD_CONFIG_DIR/authorized_keys/<name>.pub.
meshguard up
Start the meshguard daemon.
meshguard up [options]| Flag | Default | Description |
|---|---|---|
--seed | (none) | Seed peer ip:port. Can be repeated. |
--dns | (none) | Discover seeds via DNS TXT records |
--mdns | false | Discover seeds via mDNS on LAN |
--announce | (auto) | Manually specify public IP for announcement |
--encrypt-workers | 0 | Number of encryption threads (0 = serial) |
--kernel | false | Use kernel WireGuard instead of userspace |
--gossip-only | false | Run discovery/rendezvous only, no TUN/WG |
--no-tun | false | Alias for --gossip-only |
--open | false | Accept all peers (skip trust enforcement) |
--gossip-port | 51821 | Gossip/listen UDP port |
--control-path | (auto) | Control socket / named-pipe path |
--relay | false | Forward sealed frames only for clients proven by handshake |
--max-clients | 16384 with --relay, else 1024 | Relay clients tracked at once |
--client-pps | 50 | Frames per second each relay client may send |
--client-kbps | 256 | KiB per second each relay client may send |
--forward-pps | 20000 | Frames per second forwarded for all clients |
--trusted-handshake-source | (none) | Address whose handshakes are limited per port, such as a co-located browser gateway |
--legacy-wg-binding | false | Accept gossip-learned WireGuard keys for admitted clients (migration only) |
--opaque | false | Tell clients nothing about other clients (implies --relay) |
--no-delivery-status | false | Do not answer dropped frames with a 0x51 status |
--reply-pps | 5000 | Answers to pings per second |
Relay forwarding is described in docs/reference/relay-forwarding.md. With --relay, empty trust lists refuse every peer instead of accepting all; --open is the only way to accept everyone. Send SIGHUP (or the owner's control command RELOAD) to re-read authorized_keys/, trusted_orgs/ and node.cert without restarting. meshguard status includes a relay object with client counts, frames forwarded, and frames dropped by reason.
Startup sequence:
- Load identity from config directory
- Derive mesh IP from public key
- Create
mg0interface unless running--gossip-only - Assign mesh IPv4/IPv6 and set MTU (1420)
- Run STUN to discover public endpoint
- Connect to seed peers
- Enter SWIM gossip plus WireGuard event loop, or gossip-only loop
meshguard down
Stop the daemon.
meshguard downWhen a userspace daemon is running, this sends a graceful stop request through the control socket (/run/meshguard/meshguard.sock, the per-user config socket fallback, or \\.\pipe\meshguard on Windows). On Linux, if no control socket answers, meshguard down falls back to RTNETLINK teardown for the kernel mg0 interface.
meshguard status
Display the current mesh state.
meshguard statusWhen a userspace daemon is running, this queries the control socket and reports the node public key, mesh IP, and membership counts. On Linux, if no control socket answers, meshguard status falls back to RTNETLINK and kernel WireGuard Genetlink for mg0 where available.
meshguard version
Print the version.
meshguard versionmeshguard config show
Display the current node configuration.
meshguard config showmeshguard service
Manage service access control policies (port-level allow/deny rules).
meshguard service <command> [options]Subcommands
| Subcommand | Description |
|---|---|
list | List all service policies |
allow <proto> <port> | Add an allow rule |
deny <proto> <port> | Add a deny rule |
default <allow|deny> | Set default action (when no rule matches) |
show [peer-name] | Show effective policy for a peer (or global summary) |
reset | Remove all service policies |
Options
| Flag | Description |
|---|---|
--peer <name> | Target a specific peer (by alias or pubkey) |
--org <name> | Target an organization |
| (no flag) | Target the global policy |
Protocol and Port
- Proto:
tcp,udp, orall - Port: single port (
22), range (8000-9000), orall
Examples
# Global: allow SSH and HTTPS, deny everything else
meshguard service allow tcp 22
meshguard service allow tcp 443
meshguard service deny all
# Set default-deny mode (when no services/ directory exists, default is allow)
meshguard service default deny
# Peer-specific: allow Postgres for node-1
meshguard service allow --peer node-1 tcp 5432
# Org-specific: allow HTTP for all acme members
meshguard service allow --org acme tcp 80
# List all policies
meshguard service list
# Show effective policy for a peer
meshguard service show node-1
# Clear all policies
meshguard service resetEvaluation Order
Rules are evaluated in this order (first match wins):
- Peer-specific policy (by pubkey or alias)
- Org-specific policy (by org name)
- Global policy (
global.policy) - Default action (
services/defaultfile, defaults toallow)
meshguard connect
Direct peer connection via token exchange — no seed server needed. Uses STUN-based coordinated hole punching.
# Generate a connection token (share with your peer)
meshguard connect --generate
# Generate a token that expires in 30 minutes
meshguard connect --generate --in 30
# Join using a token from another peer
meshguard connect --join mg://...| Flag | Description |
|---|---|
--generate | Generate a connection token for a peer to join with |
--join | Join using a mg:// token from another peer |
--in | Token expiry in minutes (used with --generate) |
Flow:
- Peer A runs
meshguard connect --generate→ prints anmg://token - Peer A shares the token with Peer B
- Peer B runs
meshguard connect --join mg://... - Both peers perform STUN discovery, exchange endpoints, and establish a direct WireGuard tunnel
- Seeds are saved to config for future
meshguard upreconnection
Environment Variables
| Variable | Description |
|---|---|
MESHGUARD_CONFIG_DIR | Override config directory (default: ~/.config/meshguard) |
MESHGUARD_CONTROL_PATH | Control socket bind and CLI connect path (no default fallback when set) |
MESHGUARD_GOSSIP_PORT | Gossip/listen UDP port when --gossip-port is omitted. Explicit invalid values fail closed (no fallback to 51821). |
Default config directories are %APPDATA%\meshguard\ on Windows, /etc/meshguard when running as root on POSIX systems, otherwise $XDG_CONFIG_HOME/meshguard or ~/.config/meshguard.
meshguard org-keygen
Generate a new Ed25519 organization keypair.
meshguard org-keygenOutput files (in $MESHGUARD_CONFIG_DIR/org/):
org/org.key— secret key (permissions0600)org/org.pub— public key
meshguard org-sign
Sign a node's public key with the organization key.
meshguard org-sign <node-key-or-path> [--name <label>] [--expires <unix-timestamp>] [--wg-pubkey <key-or-path>]| Argument | Description |
|---|---|
<node-key-or-path> | Base64 public key string or path to a .pub file |
--name | Human-readable name for the node |
--expires | Unix timestamp when the signature expires (default: 0, never expires) |
--wg-pubkey | Base64 WireGuard public key string or path to a file. When set, emits a v2 certificate that binds node identity to this WireGuard key. |
Without --wg-pubkey, org-sign emits a legacy v1 certificate for mixed-version meshes. With --wg-pubkey, it emits a v2 certificate. Upgrade trust-enforcing peers before relying on v2-only certificates.
meshguard org-vouch
Vouch for an external node. This action auto-propagates to organization members.
meshguard org-vouch <node-key-or-path>| Argument | Description |
|---|---|
<node-key-or-path> | Base64 public key string or path to a .pub file |
meshguard org-revoke
Sign an organization certificate revocation for a node key.
meshguard org-revoke <node-key-or-path> [--reason <reason>]| Argument | Description |
|---|---|
<node-key-or-path> | Base64 public key string or path to a .pub file |
--reason | unspecified, key-compromised, or admin-removed (default) |
The command loads $MESHGUARD_CONFIG_DIR/org/org.key, writes a signed OrgCertRevoke wire message to $MESHGUARD_CONFIG_DIR/revoked/*.revoke, and queues that revocation for broadcast the next time meshguard up runs. Revocations are scoped to the issuing org: they invalidate that org's certificate or vouch for the node, not certificates or vouches from other trusted orgs.
meshguard appsend
Send an isolated application-channel message. The daemon frames the plaintext as MGAPP1 <channel> <payload> and encrypts it on the existing 0x50 path.
meshguard appsend <peer-pubkey-hex-or-b64> <channel> <message>Channel names must match [a-z0-9._-]{1,64}, for example example-v1.
meshguard apprecv
Pop the next message from one application channel. Never reads the legacy RECV queue or another channel.
meshguard apprecv <channel> [--wait <ms>]meshguard appinfo
Print {"protocol":1,"maxPayload":952} from the daemon. maxPayload is the conservative framed maximum (1024-byte 0x50 plaintext minus MGAPP1 , a 64-byte channel, and the payload separator).
See Application messaging channels.
meshguard invite
Create an invite token that lets a remote agent join this node. The seed is required: it is the public host:port of a MeshGuard node or relay that both sides can reach. There is no built-in default.
meshguard invite --seed <host:port> [--name <label>]| Option | Description |
|---|---|
--seed <host:port> | Seed the joining agent will use (required) |
--name <label> | Label for the joining agent (default: agent) |
The token (mgjoin://...) carries this node's public keys, the seed, and the label. Without --seed the command exits with an error.
meshguard join
Join a host from an invite token. Starts a --gossip-only --open daemon with the token's seed if none is running, then greets the host.
meshguard join <invite-token>A token without a seed address is rejected; ask the host for a new token made with meshguard invite --seed <host:port>.
meshguard upgrade
Upgrade meshguard to the latest release from GitHub.
Platform: Linux (amd64) only. This command downloads the
meshguard-linux-amd64asset from the latest GitHub release. It is not supported on other operating systems or architectures.Privileges: Requires write access to
/usr/local/bin/meshguard. Run withsudoif the current user does not have sufficient permissions.Service management: Uses
systemctlto stop themeshguardservice before installing the new binary and restart it afterwards. Ensuresystemdis available on your system.
meshguard upgrade
# or, if elevated privileges are required:
sudo meshguard upgradeThe command:
- Queries the GitHub Releases API for the latest tag.
- Downloads
meshguard-linux-amd64from that release. - Stops the
meshguardsystemd service. - Installs the new binary to
/usr/local/bin/meshguard. - Restarts the
meshguardsystemd service.