- Go 99.8%
|
|
||
|---|---|---|
| cmd | ||
| docs | ||
| internal | ||
| pkg | ||
| .gitignore | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
cord
cord coordinates WireGuard networks. Each network has one server, which owns the network's membership and decides which peers can see each other. Every machine that joins runs a client, which installs the network from an invite and keeps its WireGuard configuration in sync with the server.
If someone sent you an invite, you only need the client. Follow Install, then Join a network from an invite.
Install
You need:
- Linux with systemd. macOS can run cord by hand; see Running a daemon by hand.
- Go 1.26 or newer. Go 1.21 or newer usually downloads 1.26 by itself; if yours refuses, install a current release from https://go.dev/dl/.
gitandmake.- WireGuard support in the kernel, which Linux 5.6 and newer include.
From a checkout of this repository:
make build # builds bin/cord
sudo make install # copies it to /usr/local/bin/cord
On Fedora, RHEL, and related distributions, sudo's secure_path omits
/usr/local/bin, so sudo cord reports command not found. Resolve the path
before sudo resets it, for example
sudo "$(command -v cord)" client service install.
Join a network from an invite
Start the client daemon as a system service:
sudo cord client service install
This adds you to the cord group, which lets you run cord client commands
without sudo. Log out and back in for the membership to take effect, or
prefix the next commands with sudo.
Install the invite you were sent:
cord client network install invite.json
An invite works once and expires, normally 24 hours after it was created.
The command prints the network's name and your address in it, then deletes the
invite file; pass --keep-invite to keep it. You can also pipe the invite in
with cord client network install -.
Check that the network is up:
cord client network topology <network>
ping cord-server.<network>.internal
The topology shows your machine and the peers you can reach. The server is always reachable. Other peers appear once the server's admin gives you access to them.
What setup changes on your machine
sudo make install copies the cord binary to /usr/local/bin/cord.
sudo cord client service install:
- creates the
cordsystem group if it does not exist, and adds you to it (the user who ran sudo, or--admin-user); - writes
/etc/systemd/system/cord-client.service, enables it to start at boot, and starts it, waiting until the daemon is ready; - runs the daemon as root, because it creates WireGuard interfaces;
- keeps the daemon's state, including this machine's WireGuard keys, in
/var/lib/cord/data/client.db; - serves the daemon's control socket at
/run/cord/client.sock, which only root and thecordgroup can use.
cord client network install:
- generates this machine's permanent WireGuard key pair;
- briefly creates a temporary interface,
<network>-i, to redeem the invite; - creates a WireGuard interface named after the network, with a route for the network's address range through it;
- sends lookups for
<network>.internalnames to the network's server throughsystemd-resolved, on that interface only. Your other DNS settings are not changed.
cord does not change your firewall.
Troubleshooting
permission denied on /run/cord/client.sock: your login session does not include thecordgroup yet. Log out and back in, or usesudo.the cord client daemon is not running: checksystemctl status cord-clientandjournalctl -u cord-client.invite expired: ask the sender for a new invite.redeem invite with the server at …orconfirm peer with the server at …followed by a timeout: the server may be down, or a firewall may be blocking UDP to the address shown. Ask the sender to check the server.- Names do not resolve, but
pingto an address works: name resolution needssystemd-resolved, which some distributions, such as Debian, do not run by default.cord client statusshows thedns-resolvererror.
Removing cord
cord client network uninstall <network>
sudo cord client service uninstall
The service uninstall stops the daemon and removes its unit, but keeps the
cord group and /var/lib/cord. To remove everything, also run
sudo rm /usr/local/bin/cord, sudo rm -rf /var/lib/cord, and
sudo groupdel cord, unless this machine also runs a cord server.
Run a server
The server needs a public IP address that peers can reach over UDP. Start the server daemon:
sudo cord server service install
This makes the same kinds of changes as the client service install: the
cord group, the unit /etc/systemd/system/cord-server.service, state in
/var/lib/cord/data/server.db, and the socket /run/cord/server.sock. Log out
and back in to use cord server commands without sudo.
Create a network
cord server network add office 10.42.0.0/16 203.0.113.10
cord server network enable office
The arguments are the network's name, its address range, and the server's
public IP address. The server takes the first address, here 10.42.0.1.
Networks start disabled; enabling one creates two WireGuard interfaces on the
server:
office, the network itself, listening on UDP 51820;office-i, which new peers use only to redeem their invites, listening on UDP 51821.
Allow inbound UDP on both ports in the server's firewall and in any cloud
provider security group. A second network on the same server needs different
ports; see cord server network add --help.
Invite a peer
cord server registration create office bestie 10.42.0.20 -o bestie.json
The arguments are the network, a name for the peer, and the address to give
it, which must be unused and inside the network's range. Use
cord server peer list office and cord server registration list office to
see addresses in use. The invite file contains a private key, so send it
privately. It works once and expires after 24 hours, or after the duration
given with --expires, such as --expires 72h. Revoke an unused invite with
cord server registration revoke office bestie.
Let peers see each other
A peer can always reach the server. Peers see each other only through groups joined by an association. Sharing a group is not enough; a group whose members should see each other needs an association with itself:
cord server group create office friends
cord server association create office friends friends
cord server registration group add office bestie friends # before the invite is used
cord server cidr group add office laptop friends # an existing peer, by name
Associating two different groups, such as friends and servers, lets
members of each see members of the other. cord server network topology office shows the network. Clients pick up changes within two minutes, or at
once with cord client network sync <network>.
Managing the daemons
Status and logs
cord client status
systemctl status cord-client
journalctl -u cord-client -f
The same commands work for the server with server in place of client.
cord client status and cord server status show the daemon's resource tree.
Each resource reports health and lifecycle state; activity rows (↻) report
their last result and last successful attempt. State arrows show observed versus
intended state, such as failed → stopped for unfinished cleanup. Errors and
reasons appear below the tree. Add --verbose (-v) for all activities and their
attempt times and schedules, or --json for the complete status response.
Upgrading and changing options
An install will not replace an existing unit. After installing a new binary,
move the daemon onto it with --upgrade, which keeps the options the unit was
installed with and restarts the daemon:
make build
sudo make install
sudo cord client service install --upgrade
To change options such as --socket-mode or --backend, pass --overwrite
with the complete set; options left out return to their defaults. --print
shows the unit without installing it.
Because the daemon runs as root, the install refuses a binary that anyone but
root could replace, such as bin/cord in a checkout; run the copy that
make install placed. If a daemon started by hand already answers on the
control socket, the install refuses to proceed until it is stopped. Only
systemd is supported so far; --manager selects the init system explicitly.
Running a daemon by hand
sudo cord client daemon
sudo cord server daemon
A daemon run this way stores its database in data/ relative to the current
directory, so always start it from the same directory. --backend selects
the WireGuard implementation (auto, kernel, or userspace); use
userspace on hosts without kernel WireGuard. --debug enables verbose
logging. This is currently the only way to run cord on macOS.
Socket permissions
Daemon sockets default to mode 0660: root and the socket's group can connect.
The systemd units run the daemons with group cord. A daemon started by hand
with sudo gets root's group, so only root can connect unless you choose
another policy with --socket-mode:
sudo cord server daemon --socket-mode 0600 # root only
sudo -g cord cord server daemon --socket-mode 0660 # root and cord group
Private DNS
Each network serves names under .internal: <peer>.<network>.internal for
peers and cord-server.<network>.internal for the server. Each peer can
resolve only the peers it can see. Clients need systemd-resolved on Linux;
macOS clients use /etc/resolver. See docs/DNS.md for details.
Scripting
Administrative commands with --json emit their response without confirmation
text. Commands with no response write nothing on success; use the exit status
to check the outcome. Empty lists are []. When registration creation uses
--output, the invitation is written to that file and JSON mode leaves stdout
empty. Daemon logging remains text on stderr.
Development
make help # lists targets
make test
make lint
See docs/DESIGN.md for the architecture and docs/TESTING.md for test conventions.