VPN client (WireGuard)¶
Looking for the organization's gateways?
Site-to-site gateways, peers and exposures are managed with bnerd vpn gateways.
Connect this machine to your organization's WireGuard VPN. The CLI enrols the machine as a device, and b'nerd issues the tunnel configuration for it — you no longer paste a public key to an administrator or hand-maintain a vpn: block.
This page is the command reference: flags, defaults and exit codes. For what the concepts mean — which services a device may reach, who decides that, and why an enrolment is refused — see the VPN service documentation.
Your private key never leaves this machine
bnerd vpn up generates the key pair locally and writes the private half to <config-dir>/<device>.key with mode 0600. Only the public key is sent to b'nerd. The CLI never prints, logs or transmits the private key, and it deletes that file only when you pass --regenerate-keys.
Connect¶
On the first run this enrols the machine against the organization's ready WireGuard gateway, fetches the tunnel configuration, writes <config-dir>/<interface>.conf and brings the interface up. Afterwards the existing enrolment is reused, so running it again is cheap and idempotent.
Requires root/sudo
Bringing the interface up needs elevated privileges. Enrolment itself does not.
Flags¶
| Flag | Type | Default | Description |
|---|---|---|---|
--device | string | this machine's hostname | Device name. A name you pass must already be a valid DNS-1123 label; the hostname default is sanitised into one. |
--gateway | string | the single ready gateway | Gateway ID to enrol against. Required when the organization has more than one ready WireGuard gateway. |
--interface | string | bnerd0 | WireGuard interface name |
--regenerate-keys | bool | false | Discard the local private key, generate a new pair and enrol a new device |
Choosing a gateway¶
bnerd vpn up discovers gateways through the member-readable enrollable-gateway list, so a plain org member needs no gateway rights. A static access token needs the vpn_devices:write scope (it includes read; scopes nest read < write < manage < *) — not vpn_gateways:* — and it must be an org-wide token: gateways and devices are organization-level, so a token pinned to one project is refused. A token without the scope fails with:
Error: Access token does not have permission for this action — this token lacks the `vpn_devices:write` scope; create an org-wide token with that scope (or a wider level: read < write < manage < *)
If several gateways are ready, the CLI refuses to guess and names the candidates:
Error: this organization has several ready WireGuard VPN gateways — pass --gateway
with one of: gw-1 (hq-wg, ready), gw-2 (lab-wg, ready)
When enrolment is refused¶
| Condition | Exit | What it means | What to do |
|---|---|---|---|
| gateway still provisioning | 4 | The gateway has not reported its key, address and service CIDR yet | Wait a few minutes and run again |
| device limit reached | 5 | The gateway is at its device limit (50 per gateway by default, at most 60) | Revoke a device you no longer use, or ask b'nerd to raise the limit |
| no free address | 5 | The gateway's road-warrior address range is full | Revoke a device you no longer use, or contact b'nerd |
| service CIDR unsupported | 5 | The gateway's CIDR cannot host road-warrior devices | Contact b'nerd to set a road-warrior range |
| device revoked | 1 | Someone revoked this enrolment | Run bnerd vpn up again to re-enrol; your local key is kept |
See Exit codes for the full mapping.
Disconnect¶
Purely local — the device stays enrolled, so bnerd vpn up reconnects without touching the API. To withdraw a device's access for good, revoke it.
Check status¶
bnerd vpn down and bnerd vpn status both accept --interface.
Devices¶
list shows your own devices: id, name, gateway, tunnel address, health, traffic counters and whether the device is active or revoked. --all is an org-admin view of every device in the organization and adds the owner's email; a plain member asking for it gets a clear refusal rather than a silently narrowed list.
Health and counters are not live
health, last_handshake and the byte counters in devices list are what b'nerd last read from the gateway, and b'nerd reads it when it reconciles the gateway (for example after an enrolment or a revoke) — not continuously. A device can show unknown or an old handshake while its tunnel is fine. For the live state of this machine's tunnel use bnerd vpn status (it reads the local interface).
revoke cuts the device off at the gateway and keeps the record, so who had access and when it was withdrawn stays visible. It is idempotent, and it does not touch the private key on the device itself — that machine can enrol again with bnerd vpn up.
There is no devices enrol command on purpose: a device is enrolled by the machine that owns it, because the private key is generated there.
Exit codes¶
Every VPN command uses the CLI-wide mapping, so a script can branch on the outcome without parsing messages.
| Code | Meaning | When you will see it here |
|---|---|---|
0 | Success | |
1 | Failed, read the message | The device was revoked (410). Re-run the same command to re-enrol. Also any local failure: an unreadable key file, an unusable interface name. |
2 | Not found | A device id that does not exist, or one you may not read |
3 | Not permitted | devices list --all as a plain member |
4 | Conflict, retry later | The gateway is still provisioning — on enrolment and on fetching the configuration alike |
5 | Refused | The gateway's device limit is reached, its address range is full, or its service CIDR cannot host devices |
6 | Server error |
Code 4 is the one worth branching on: it is transient and the identical command will succeed once the gateway finishes reconciling. Code 5 will not resolve on its own — someone has to revoke a device or change the gateway.
Revocation is deliberately not a code of its own. The remedy is to run the same command again, which the message already tells you, so it falls into the generic 1.
Configuration¶
| Key | Default | Description |
|---|---|---|
vpn.config-dir | ~/.config/bnerd/wireguard | Where key files and generated .conf files live |
vpn.interface | bnerd0 | Default interface name |
vpn.cleanup-config | false | Remove the generated .conf on vpn down |
One tunnel per interface, one key per device¶
The two files are keyed differently, on purpose:
- the private key is
<config-dir>/<device>.key— it is the device's identity, so each device keeps its own; - the tunnel config is
<config-dir>/<interface>.conf— it describes one live interface, and only one device can occupy an interface at a time.
So running bnerd vpn up for a second device on the same interface replaces that .conf while leaving both key files intact. Give the second device its own --interface if you want both tunnels at once.
Enrolments outlive the config file
Replacing the .conf does not un-enrol anything. Both devices stay enrolled at b'nerd and both keep counting against the gateway's device limit until you bnerd vpn devices revoke them.
Examples¶
# Connect this laptop (enrols on first run)
sudo bnerd vpn up
# Pin the device name and the gateway
sudo bnerd vpn up --device bernd-laptop --gateway gw-1
# Second tunnel on its own interface
sudo bnerd vpn up --device bernd-lab --interface bnerd1
# See what is enrolled, then retire an old phone
bnerd vpn devices list
bnerd vpn devices revoke dev-7f3a
# Start over from a fresh key pair
sudo bnerd vpn up --regenerate-keys
Deprecated: bnerd up / bnerd down / bnerd status¶
The top-level bnerd up, bnerd down and bnerd status are aliases of bnerd vpn up, bnerd vpn down and bnerd vpn status: same flags, same behaviour, same stdout. They print a one-line deprecation notice on stderr and will be removed in a future release. Use the bnerd vpn forms in new scripts.
| Legacy | Replacement |
|---|---|
bnerd up [--device] [--gateway] [--interface] [--regenerate-keys] | bnerd vpn up (same flags) |
bnerd down [--interface], bnerd up --down | bnerd vpn down |
bnerd status [--interface] [-w], bnerd up --status | bnerd vpn status |
bnerd up --generate-keys | not needed: bnerd vpn up generates the device key on first run; --regenerate-keys replaces it |
bnerd up --show-peer-info | not needed: the public key is enrolled automatically; bnerd vpn devices list shows the device |
The two removed flags fail with an error that names the replacement. The static vpn: keys the old client read (server-endpoint, server-public-key, private-key, public-key, client-address, allowed-ips, dns) are ignored; the aliases read only the three keys bnerd vpn up reads.
See also¶
- VPN service documentation — the concepts behind these commands: the gateway's
device_authorizationdefault and the narrow-only per-device override that decides what a device may reach, the per-gateway device limits, and what each enrolment refusal means for the product. bnerd vpn gateways— the org-admin side: gateways, peers and exposures.
Guides¶
- VPN Quick Start — Full setup walkthrough
- Multiple VPN Connections — Running multiple VPNs simultaneously