Signing in the desktop app
The desktop app signs in to this console by pairing a key, not by pasting a token. There is no token to copy, and no way to sign in while the console cannot be reached: when it is down, the app can only say so.
What happens when you press Sign In
Section titled “What happens when you press Sign In”- The app makes an ed25519 keypair the first time it signs in. The public
half is an
ssh-ed25519line, the kindssh-keygenwrites. Nothing in this API takes the private half: a claim carries the public key and a signature made with the private one. - The app opens
/connect?name=<device>&key=<public key>in your browser. If you are not signed in to the console, you sign in first and land back on that page. - The page shows which account you are signed in to, the device’s name, and
the key’s fingerprint — the
SHA256:stringssh-keygen -lfprints. The app shows the same fingerprint beside Sign In. Compare them. - Pressing Connect approves the key for your account. Nothing is approved by opening the page; only the button does that.
- Meanwhile the app asks the API every couple of seconds whether its key has
been approved, proving it holds the private half by signing each request.
The first approved request is answered with a
tor_token, exactly once, and the app uses it as its API key from then on.
The app gives up after five minutes; an approval nobody collects lapses after ten. Press Sign In again to start over.
What pairing proves, and what it does not
Section titled “What pairing proves, and what it does not”Pairing proves that the app holding the private key is the one you approved, and that it approved nothing you did not press Connect for. It does not make a link safe on its own: a page can only be reached with your session, a visit pairs nothing, and the fingerprint is on the face of the page so you can check it against the app before you press anything. If the fingerprint on the page is not the one the app shows, do not press Connect — the key on the page is not the app’s.
The public key travels in the page’s address, so it is in your browser history and in any proxy’s log on the way. That is fine: it is public, and knowing it lets nobody sign in. The API never asks for the private half and has nowhere to put one.
The name and address the app shows for you are what your identity provider asserted the last time you signed in to the console. A change made at the provider reaches the app the next time you sign in here, not on its own.
Revoking a device
Section titled “Revoking a device”Every paired device is listed on the API keys page under Devices, with its fingerprint and when its key was last used. Revoke signs that app out and invalidates its key immediately; the next request it makes is refused. The app can connect again by pairing a new key.
A device’s key does not expire on its own. The Devices list is the control.
Signing out from the app
Section titled “Signing out from the app”Sign out in the app is the same revocation from the other end. The app gives up the token it holds, and the console revokes the device that token was minted for, so the row leaves the Devices list at that moment.
Signing in again pairs a new key, because the console hands a key its token once: a device’s fingerprint after signing back in is not the one it had before. That is a new row and not the old one returning.
An app that never got to sign out (it was quit while signed in, or the console could not be reached) leaves its row live, and that row is what Revoke on the Devices list is for.
For the API
Section titled “For the API”The app’s half of the flow is two routes, POST /v1/device/claim and
DELETE /v1/device, both described in the API reference. The
sign-out names no device: the bearer it carries is the device. The message the
claim signs is published in the repository’s wire contract, so a client reads
it rather than retyping it.