Connecting to a machine¶
Approving a computer: the 6-digit check¶
When you approve a computer, two keys are swapped: your browser key goes to the computer, and the computer's ID goes to your browser. Our website carries both. So how do you know we (or anyone in between) didn't swap them for fakes?
Both screens show a 6-digit pairing code made from both keys. If anything was swapped, the codes come out different.
your browser your computer
+---------------------+ +---------------------+
| Pairing code | | Pairing code: |
| 482 190 | same? | 482 190 |
+---------------------+ | Does your browser |
| show this? [y/N] y |
+---------------------+
Only after you type y on the computer itself does it start trusting your browser.
Connecting: the locked conversation¶
you click Connect
|
v
browser --(1) "connect me to home-pc"--> website: is this YOUR computer?
yes -> tells home-pc: "a browser wants you"
home-pc opens a second line out to the website
website joins the two lines together
|
v
browser <========== locked conversation ==========> home-pc
checks: "is this really home-pc?" checks: "is this a browser I approved?"
(its ID must match the one you (your key must be on its list)
approved)
Everything you type and everything that comes back is locked by your browser and your computer, not by us. We pass along the locked pieces.
If something's wrong¶
- Your computer's ID doesn't match: your browser stops and warns you. It never quietly trusts a new ID.
- Your browser isn't on the computer's list: the computer refuses ("Login failed").
- Someone steals the computer's connection token: they still can't pretend to be your computer, because they don't have its ID key.
Enrolment (device-code flow + pairing code)¶
roamd login hub browser (logged in)
----------- --- -------------------
create host key H (Ed25519)
register H, get a short code ------>
print approve link open link, see
machine name + fp(H)
approve: send B.pub
pin fp(H) in vault
collect B.pub + device token <------
pairing code = 6 digits from SHA-256 over fp(H) and fp(B)
user compares with browser, types y
only then: trust B.pub, save settings (sealed)
If the hub substituted H or B, the two sides would compute different pairing codes. The
user rejects, and nothing is written. Approval codes are short-lived, single-use and
rate-limited.
Tunnel and session setup¶
roamd run --(outbound TLS WebSocket, device token)--> hub (kept open)
browser --(TLS WebSocket, session cookie, origin check)--> hub
hub: does this account own the machine? is it online?
hub asks the machine to open a second stream
hub splices the two streams (bytes only)
browser SSH client <==== SSH, end to end ====> roamd SSH server
- Browser side: the machine's host key must match the pinned identity in the vault. There is no silent trust-on-first-use. Authentication uses the vault's Ed25519 key.
- Machine side: only keys the user confirmed on this machine are accepted.
- Algorithms: post-quantum hybrid key exchange (ML-KEM-768 + X25519), falling back to Curve25519; ChaCha20-Poly1305 or AES-GCM; strict key exchange.
What the hub can do if it's compromised¶
| Attack | Outcome |
|---|---|
| Read or change the shell session | No: SSH is end to end, and changes break its integrity check |
| Pose as the machine with a stolen device token | Browser refuses: no host private key, pin mismatch |
| Add its own key to the machine | No: only keys confirmed on the machine via matching pairing codes |
| Deny or drop service | Yes (it's the relay) |
| Serve malicious web-app code | Possible: see limits |
Tunnel hardening (agent)¶
The agent only talks to the hub over HTTPS. It validates and size-limits everything the hub
sends, limits how many streams can be open at once, drops a hub that goes silent, and
reconnects with randomised back-off. If the hub rejects its token, roamd run stops.