How it works
- Members are your devices. Each holds a device key that resolves peers, gossips configuration and authenticates machine to machine. A device key never opens a shell.
- Operators are the keys that open shells. They sit in your ssh-agent, and every member authorizes them.
- Guests are machines you reach but don't enrol, such as a client's server. Mesh pins their host key and routes to them, and installs nothing on them.
- Configuration is a git repo of public material only: node metadata, public keys and the operator list. Members gossip it to each other. Private keys never leave the device that made them.
Getting started
Create the key that opens your shells, and keep it in an agent:
ssh-keygen -t ed25519 -C me -f ~/.ssh/op
ssh-add ~/.ssh/opOn the first device:
mesh init desk --operator ~/.ssh/op.pub
mesh scheduleTo add a laptop, run this on desk:
mesh invite --serveIt prints a one-use token and an address. The join runs over TLS 1.3 keyed by that token. On the laptop:
mesh join book 192.168.1.10:43117 --token <token>ssh desk and ssh book now work from either machine.
Reaching a peer
ssh <peer> tries each route in turn until one answers:
- the last address that worked
- a fixed address from the peer's configuration
- the addresses in the peer's signed record
- a relay through another member that can reach it
- a sweep of the local subnets
Every candidate must present the host key pinned for that peer, so the wrong machine at the right address is refused. Mesh has no rendezvous server, so two peers with no shared member and no fixed address stay unreachable.
Guests
mesh guest web01 198.51.100.7 --user deploy
ssh web01A forward alias reaches a port only the guest can see:
mesh guest web01 198.51.100.7 --user deploy --fwd db:15432:127.0.0.1:5432
ssh web01-dbA guest with a new address on every launch is declared --dynamic, and a mesh-resolve-<name> program on PATH supplies its address and host key.
Access control
- A device key on any disk is limited to five commands: ping, record exchange, a port probe, and git fetch and push of the configuration repo. It can't open a shell.
- Changing the operator list needs a signature from a key already on it.
- Every member checks every configuration commit it takes in, and rejects a commit that touches anything outside its own entry unless an operator signed it.
- Mesh watches
authorized_keysoutside its own block and warns when that part changes. - Losing every operator key means starting the mesh over, so keep a spare offline.
Commands
| Command | What it does |
|---|---|
init <name> --operator <pubkey> | name this device and create its identity |
invite [--serve] | mint a one-use join token |
join <name> <addr> --token <tok> | join a mesh as <name> |
adopt <user@host> | pull a node in from this side |
guest <name> [addr] | declare a dial-only node |
operator | show, add or remove a shell key |
evict <node> | remove a member or guest |
status | show members, guests, forwards and operators |
schedule | install the background service |
Requirements
bash, git 2.38 or later, the OpenSSH client and server, openssl, tar, timeout and flock. nmap enables the subnet sweep. On macOS, Homebrew's flock and coreutils supply the last two. Both ends of a join need a reachable sshd.
What mesh doesn't do
Mesh routes no traffic and adds nothing ssh doesn't already do. It isn't a VPN, a secret store or an access-request system: it authorizes keys you've already decided to trust.