Pairing a device

Pairing is how a new phone, tablet, or browser earns the right to talk to your daemon. It happens once per device and takes a few seconds. There are two ways to do it, and both reach the same key exchange with the same protection.

What pairing is

A daemon will not talk to a device it has never met. Pairing introduces the two: the device proves it holds a fresh pairing secret, both sides run a key exchange, and the daemon hands the device a private token to use from then on. After that the device is on the allow-list and every message between them rides an encrypted session. You only do this once per device. Lose the device or finish with it, and you revoke its token without touching any other.

Option 1: scan the QR

When the daemon starts it prints a QR right in the terminal. Open your phone camera or the codeout app and point it at the QR. It carries the daemon address and a short-lived pairing secret. Your device runs the key exchange, receives its own device token, and is paired. Nothing to type. This is the fast path, and it also gives you the man-in-the-middle check for free, which the next section explains.

Option 2: type the code

No camera, a QR that will not scan, or pairing across a screen-share where a camera is useless? The daemon also prints a human-typeable pairing code. Enter it in the app along with the daemon address and you get the same result as scanning.

~/projects/app
$ codeout
codeout -> http://localhost:8400
pair a device: scan the QR, or type this code
  K7QM-3FBR-9TZX
  expires in 5:00

In the app, choose to pair manually, enter the daemon address (for example http://localhost:8400 or your tailnet address), and type the code. The app runs the same libsodium key exchange the QR triggers and stores the device token it gets back.

What the code looks like

The pairing code is twelve characters of Crockford base32, grouped as XXXX-XXXX-XXXX so it is easy to read aloud and easy to type. Crockford base32 drops the letters that get confused with digits, so there is no squinting at whether that is a zero or an O, a one or an I. Three properties matter:

single-use
it pairs exactly one device, then it is spent. You cannot reuse it to sneak a second device on.
short-lived
it expires five minutes after the daemon shows it. A code from yesterday is dead.
human-typeable
twelve unambiguous characters in three groups of four, so reading it down a phone line actually works.

Confirm the daemon fingerprint

After the key exchange, the app shows a short fingerprint of the daemon you reached, four and four like ZMYR-C1HG. The daemon prints the same fingerprint in its terminal. Glance at both and check they match.

~/projects/app
device paired: phone-15
daemon fingerprint: ZMYR-C1HG
  confirm this matches what the app shows

If they match, you are talking to your daemon and nothing in between. If they do not match, something is sitting in the path pretending to be your daemon, and you stop right there.

Why this defeats a man-in-the-middle

The fingerprint is derived from the daemon's real key material, which an attacker cannot fake. A machine that relays your pairing to slip itself into the middle would have to present its own keys, so the fingerprint your app computes would not match the one your terminal prints. The mismatch is the tell. Scanning the QR gives you this protection automatically, because the QR carries the daemon's own key material and the app verifies it without you lifting a finger. The manual flow simply makes the same check visible so you can do it by eye, which is exactly what you want when you typed the address yourself and a typo could have sent you somewhere unexpected.

What happens under the hood

  1. The daemon offers a pairing secret.

    Short-lived and single-use, encoded in the QR and printed as the typeable code.

  2. Your device runs a key exchange.

    It presents the secret and performs a libsodium crypto_kx exchange with the daemon.

  3. The app shows the daemon fingerprint.

    You confirm you reached the right machine and not an impostor in the path.

  4. The daemon issues a device token.

    The device stores it; the daemon records it in its allow-list against a name you can recognise.

  5. Every later message rides an encrypted session.

    The device authenticates with its token and the channel is end-to-end encrypted from there on.

Note

The pairing secret, QR or typed, is single-use and expires in five minutes. A photo of an old QR or a code from last week is worthless. To add another device, generate a fresh one.

Add another device

From a device that is already paired, or from the daemon terminal, request a new pairing QR or code and use it on the new device. Each device ends up with its own token, so you can tell them apart in the device list and revoke them one at a time.

Revoke a device

Lost a phone, or done with a borrowed laptop? Revoke its token. The daemon drops it from the allow-list immediately and that device can no longer connect or decrypt anything new. Other devices are unaffected.

terminal
$ codeout devices
phone-15      paired 2d ago    active
work-laptop   paired 6h ago    active
$ codeout devices revoke work-laptop
revoked. work-laptop can no longer connect.