Pair a Machine in 30 Seconds Without SSH Keys (Capability Tokens)
The tray icon mints a 16-byte random pairing token with a 600-second TTL. The user clicks 'Pair this machine,' the browser jumps to the dashboard, the token is consumed in a constant-time compare, and a Firebase ID token is exchanged for a daemon session. No SSH keys to copy, no IPs to remember, no ports to forward.
SSH key pairing has aged well — for a 1990s threat model. The flow is "generate keypair, copy public half to the server, server now trusts you forever." It works. It's also five steps and three terminals if you've never done it.
For a daemon that's already locally authenticated to your account (Google sign-in, Firebase ID token), there's a faster path: capability tokens issued by the local UI.
The 30-second flow
- Open the dashboard at
celistra.devin any browser. Sign in with Google. - On the machine you want to pair, click the Celistra tray icon → Pair this machine.
- Browser jumps to
celistra.dev/pair?token=<random>. - Done. Machine is paired.
The whole exchange takes <1 second once the browser navigation lands.
Why a token, not a QR code?
The token works for the same-device case (you're at the machine). For different-device pairing (your phone driving a friend's laptop), a QR encoding the same token works — the dashboard shows it as a QR after generation.
The crypto
// Tray click
token := make([]byte, 16)
crypto/rand.Read(token)
expiry := time.Now().Add(600 * time.Second)
storeChallenge(token, expiry)
openBrowser("https://celistra.dev/pair?token=" + hex.EncodeToString(token))
// Dashboard hits POST /v1/auth with the token + Firebase ID token
// Daemon validates Firebase ID token offline against Google's JWKS,
// then constant-time compares the pairing token, then burns it.
Three properties matter:
crypto/rand, notmath/rand. The token must be unguessable.- Constant-time compare, not
==. Don't leak length-prefix info via timing. - Single-use, TTL'd. The token is good for one pairing within 10 minutes. After that, click again.
What the token authorizes
The token by itself does nothing. The dashboard sends both the token AND a Firebase ID token. The daemon validates both:
- Firebase ID token: signature checked offline against Google's JWKS (cached locally),
audmatches our project,issmatcheshttps://securetoken.google.com/<project>,expis in the future. - Pairing token: constant-time match against the in-memory challenge.
If both pass, the daemon writes the user's UID into cfg.AllowedUIDs. Subsequent requests authenticate via Firebase ID token alone — no token needed.
What this replaces
| SSH key flow | Capability token flow |
|---|---|
Generate keypair on client (ssh-keygen) | Already done — Firebase identity |
Copy public key to server (ssh-copy-id) | Click tray button |
Add server to ~/.ssh/config | Auto-added to fleet view |
| Open port 22 in firewall | Daemon binds 127.0.0.1 only |
Test (ssh user@host) | Works immediately |
Edge cases
Browser is already at celistra.dev signed in as the wrong account. The pair URL accepts the token + the current Firebase ID token. If it's the wrong account, the daemon's AllowedUIDs ends up adding both — user can revoke from the tray. Practically, sign out first.
Token expires before user clicks. Click "Pair this machine" again. New token, new TTL.
User pairs the same machine twice. Idempotent — adds the same UID to AllowedUIDs if not present.
Compromised device. "Revoke all sessions" in the tray calls a Cloud Function that runs admin.auth().revokeRefreshTokens(uid) against the account. Every Firebase ID token mints from a refresh token; revoke them all and the daemon's offline JWKS check still accepts existing ID tokens until they expire (max 1 hour). Ride out the hour, or rotate the daemon secret too.
What we ship
This is the exact pairing flow Celistra uses. The tray code is in the daemon repo (celistrad/tray.go) and is short enough to read in one sitting. The dashboard side (src/pages/Pair.tsx) is similarly small. The whole thing is a few hundred lines of code.
FAQ
Does this work without internet?
The pairing step needs internet (Firebase Auth). After pairing, normal use is LAN-direct (no internet) when the dashboard and the daemon are on the same network.
Can a malicious local app steal the pairing token?
If the malicious app can read the daemon's memory or hit 127.0.0.1:33120, yes. The token's defense is that it's single-use and short-lived. The bigger defense is don't run untrusted code as your user.
How is this different from OAuth device-code flow?
OAuth device-code is a similar pattern (out-of-band display of a code, in-band consumption). This is the same shape but the 'device' is the daemon and the 'authorization' is just 'user is signed into the dashboard.' Simpler because the trust comes from the OS user, not from a remote server.