Skip to content

Installation

Setting up roamd takes three steps: make an account, install roamd on a computer, and approve that computer. It takes about five minutes.

 1. Account            2. Install               3. Approve
 (in your browser)     (on the computer)        (both together)

 app.roamd.co.uk  -->  paste one command  -->  roamd login
 email + password      into its terminal       click Approve
 save recovery key                             check the 6 digits match
                                               type y

The web address is now app.roamd.co.uk. Your account and paired computers stay on the same service. Sign in again with your master password; add a new passkey and allow notifications again if you use them. Browser-only settings and an installed app may need setting up again at the new address.

Older roamd installations can keep connecting through the previous address during the handover. A future daemon release will update their saved address while keeping their pairing. The web change alone does not update installed daemons.

 browser -> app.roamd.co.uk -> same account and computers
 older roamd -> previous address -> same service

1. Make an account

  1. Go to app.roamd.co.uk and enter the preview access password we gave you.
  2. Choose Create account. Enter your email and a master password (at least 10 characters; a few random words work well).
  3. Check your email for a 6-digit code from roamd and type it in. This proves the email is yours; the account only exists once you do. No email? Check your spam folder, or choose Email me a new code. (If that address already has an account, the email says so instead.)
  4. You'll see a recovery key like ABCD-EFGH-… (9 groups of 4). Write it down or save it in your password manager. If you ever forget your master password, it's the only way back in. We can't reset it for you.

2. Install roamd on the computer you want to reach

Open a terminal on that computer and paste one of these. You don't need to be an administrator.

Linux or Mac (Terminal):

curl -fsSL https://app.roamd.co.uk/install.sh | sh

Windows (PowerShell):

irm https://app.roamd.co.uk/install.ps1 | iex

The installer checks the download against the published checksum before installing it, and stops if it doesn't match. (That catches a damaged or incomplete download. The checksum comes from the same website, so the signature check on later updates is the stronger protection; see Updating roamd.) You can also copy these commands from Open sessions → Add a machine in the app.

When it has finished installing, the installer goes straight on to step 3 for you. It prints a boxed message, "Next: registering this machine, if it isn't already. Starting roamd login for you", so you can see that the second step has begun. You don't type roamd login yourself. This happens every time you run the installer: if the computer is already registered, roamd says so and leaves it alone, and if roamd was installed but never logged in (or you logged out), you go through the whole setup. If there's no keyboard to ask you on, it tells you to run roamd login --hub https://app.roamd.co.uk yourself.

tmux (Linux and Mac). roamd keeps your sessions in tmux, so they survive a dropped connection. If the computer doesn't have it, the installer deals with it before the login step. On a Mac, if you're at the keyboard, it asks "Install tmux with Homebrew? [Y/n]"; press Enter to say yes. If Homebrew isn't there it first asks "Install Homebrew first?" (Homebrew's own installer; it asks for your Mac's password and can take a few minutes), then tmux. If you say no, or an install fails, or there's no keyboard to ask on, the last thing the installer prints is how to get tmux yourself (brew install tmux, with Homebrew's address if you don't have it, or sudo port install tmux with MacPorts; on Linux, sudo apt install tmux). You can skip it: roamd then gives you a plain shell.

Permission questions on a Mac. roamd itself never looks at your screen, camera or microphone. A program you run in a session might, though (an AI app taking a screenshot, for example). If it does, the Mac asks on its own screen. The question names that program, or tmux (which runs your sessions), not roamd. Say yes only if you know which program wants it and why. Saying no never stops roamd working.

 a program in a session asks to record the screen
   |
   v
 the Mac asks on its own screen, naming that program (or tmux), not roamd
   |-- Deny  --> only that program goes without; roamd and your sessions carry on
   '-- Allow --> that program may record the screen from now on

tmux on Windows (psmux). Windows has no tmux of its own, so roamd uses psmux, a tmux that runs on Windows itself (it installs as tmux.exe). If the PC doesn't have tmux, the Windows installer asks "Install psmux with winget? [Y/n]". If you say no, there's no winget, or it fails, the last thing printed is how to install it yourself (winget install psmux, or with Scoop). Sessions that psmux already has show up in the list when you connect, as tmux sessions do elsewhere. Bringing sessions back after a restart works on Windows too, with psmux's own versions of the two add-ons (see below); they need PowerShell 7 (the installer offers it with winget if it's missing; it looks on the PATH and in the usual install places: Program Files, your user folder, the Store, a preview, dotnet and Scoop). Not yet tried on a real Windows PC.

Updating psmux on Windows.

Two screens on one session (roamd and a psmux window at the PC) need psmux 3.4.0 or later; 3.3.8 flickers. Check yours with psmux -v. winget install psmux may not have the newest version yet, so use the zip from the psmux releases (psmux-v3.4.0-windows-x64.zip: psmux.exe, pmux.exe, tmux.exe). Building with cargo install psmux needs Visual Studio's C++ tools and fails without them (msvcrt.lib).

1. Find old copies:      Get-Command psmux -All | Select Source
2. Remove them:          winget uninstall --id marlocarlo.psmux   (and delete any
                         .cargo\bin\psmux.exe; an old copy earlier on the PATH wins)
3. Unzip to:             %LOCALAPPDATA%\psmux-3.4.0
4. Put it on the PATH:   [Environment]::SetEnvironmentVariable("Path",
                           "$env:LOCALAPPDATA\psmux-3.4.0;" +
                           [Environment]::GetEnvironmentVariable("Path","User"), "User")
5. Open a NEW PowerShell, check:  psmux -v     (3.4.0)
6. Stop the old server:  psmux kill-server
7. Sign out of Windows and back in, so the roamd service sees the new PATH
8. Rejoin the session from roamd

psmux -v shows "not recognized" in the window you did this in? That window predates the PATH change: open a new one. Not yet tried on a real Windows PC beyond this.

Where it goes, so typing roamd just works:

 who runs it                    roamd goes to             on the PATH?
 ---------------------------    ----------------------    ---------------------
 you (Linux, Mac)               ~/.local/bin              added for you
 root (Linux, Mac)              /usr/local/bin            already, for everyone
 you (Windows)                  your AppData folder       added for you
 Administrator (Windows)        Program Files\roamd       added, for everyone

Running the Windows installer again on a computer that already has roamd updates it: it stops the old roamd, puts the new one in place, and starts it again by itself.

If it was added for you, open a new terminal (or run the export PATH=… line the installer prints) before typing roamd.

Running as root or Administrator? roamd refuses to log in as the all-powerful account unless you ask for it with --allow-root, because every browser you approve would then get full control of the computer. The installer's automatic roamd login can't be told that, so as root it stops with that message; run roamd login --allow-root yourself if that's what you want.

3. Approve the computer

  1. On the computer, roamd login starts by itself when the installer finishes. (If you need to do it again later, run:)

    roamd login
    

On a Linux desktop, roamd locks its keys in your desktop keyring and says so. It then only starts while you're logged in to the desktop. For a computer that must be reachable after a restart with nobody logged in, run roamd login --no-keyring instead. 2. It prints a link. Open it in your browser (logged in to roamd) and click Approve. 3. Your browser and the computer each show a 6-digit pairing code. Your browser then says Not finished yet: go to the computer's terminal to compare the codes and finish. If they match, type y on the computer. If they don't, type n: something is wrong, and nothing is trusted. If your browser shows no code and says to type n, do that too. Your browser can't see what you type there. Once the computer has connected, the page notices by itself, says the computer is approved and takes you to Open sessions. 4. That's it. Once you type y, roamd sets itself up to run all the time, so there is nothing to start by hand. It ends with Done. This browser can now open a terminal here.

The computer now shows as online in Open sessions.

Reinstalling a computer you already have. A fresh install is a new identity, so it would normally show up as a second computer. If you already have one with the same name, the approval page says so and offers Replace name. Choose that and the new install takes over the old entry: same place in the list, same tab, same sharing. Choose Add as a new machine if it really is a different computer. Replacing is only offered for your own computers, and you still compare the pairing codes as usual.

Signing in to Codex on a server

After installing Codex through roamd, choose Log in to Codex. It uses a device code, so the server does not need a browser or a desktop. Open the address shown in your own browser, sign in and enter the one-time code displayed in the terminal.

Log in to Codex -> server shows a link and code
  -> open link in your browser -> enter code -> server signs in

If device-code login is unavailable, enable it in your ChatGPT security settings, or ask your workspace administrator to allow it. See OpenAI's headless login guidance.

4. Running all the time

Setting up in step 3 already did this for you. It's the same as running:

roamd service install

roamd now starts when you log in to that computer, and restarts if it ever crashes. (If you set it up as root with --allow-root, it's a system service that starts when the computer boots, with nobody logged in.) On Linux, Mac and Windows it also sets up two tmux add-ons (tmux-resurrect and tmux-continuum, or psmux-resurrect and psmux-continuum on Windows; they need an internet connection to fetch) so your tmux sessions come back after the computer restarts, or after you log in again on Windows, screen text included; turn that off with roamd tmux-restore off. If that step fails it says so, and the rest still works. Restarting or updating roamd leaves tmux running on Linux. (Set it up with an older roamd? Run the command once more.) On a Linux server that nobody logs in to, it also prints one extra command to keep it running. If roamd's keys are in your Linux desktop keyring, it waits for you to log in to the desktop, then connects by itself.

If that last part couldn't be done, login still worked, and roamd tells you so and says to run roamd service install to try again (or roamd run to start it just for now).

Removing roamd from a computer

On the computer, run:

roamd uninstall

It asks first, then takes the computer off your account, stops and removes the background service, takes roamd's tmux add-on lines out of your tmux config, deletes roamd's settings and keys, and deletes the roamd program. Your tmux sessions and your files are not touched. It ends with a list of what was done, and says so plainly if a step couldn't be. (On Windows a running program can't delete itself, so it tells you which file to delete yourself. As root, add --allow-root.)

 roamd uninstall
    |
    v
 "Remove roamd from this machine?"  -- n --> nothing changed
    | y
    v
 off your account -> service removed -> settings deleted
                                      -> program deleted

To skip the question, add --yes. If the computer can't reach the internet, it still removes everything on the computer; remove it from Open sessions afterwards.

Setting up a server you reach with SSH

If you can log in to a Linux or Mac server with SSH (a password or a key), roamd can do steps 2 to 4 there for you, either from this website or from your own computer.

From the website

In Open sessions → Add a machine → Server (SSH), enter the server's address, your username and your password (or paste a private key), then Set up.

 YOUR BROWSER                 our website            your server
 runs SSH itself  -- locked -->  passes the  -- locked -->  SSH port
                                 bytes along
 1. shows the server's fingerprint: you check it matches
 2. only then logs in (your password stays locked inside SSH)
 3. installs roamd there, which asks to be approved
 4. you click Approve; the page compares the two pairing codes
    itself, and shows you it doing so
 5. roamd starts as a service on the server

The dialog shows only the step you are on (a row of four marks the way: Your server, Check it, Install, Approve), so the address, username and password go out of sight once sent and you never have to scroll to find what to do next. When the pairing check finishes, it stays up for a moment so you can read the result. Afterwards Finish and Finish + New Session are the only things left.

The server's own messages appear in a terminal window marked Live output from you@your-server. You don't type anything there: the page answers for you.

Why the page can check the codes itself. The pairing code proves nobody, not even roamd's website, swapped anything while your browser and server were introduced. Here the two codes reach your browser by two different routes:

 your server ===== straight, sealed SSH =====> this browser
     |            (the code it worked out)        ^
     |                                            |
     +---> roamd's website ----------------------+
           (its ID and a secret; the browser works
            out its own code from them)

 same code?   yes: the page tells your server "yes, trust it"
              no:  the page tells it "no"; nothing is trusted

The SSH route is the connection you checked with the fingerprint, so roamd's website can't change what comes along it. You'd only be comparing two things the page already has, so it compares them for you: a big green The codes match or a red The codes are different. If they differ, don't go on from that network. The check runs slowly enough to watch, on a computer or a phone: each code travels its route on the diagram, then the two are compared digit by digit.

  • Check the fingerprint. It's how you know you've reached your real server and not an impostor. Your hosting provider usually shows it; or, on the server, it's what ssh-keygen -lf prints for the files in /etc/ssh/ ending _key.pub. If it doesn't match, choose It doesn't match: nothing is sent.
  • A private key is safer than a password. Even an impostor that got past the fingerprint check couldn't use a key login to get into your server. Keys with a passphrase work too; the passphrase is used only inside the page.
  • Nothing you enter is saved, unless you tick Save these login details in my vault. It starts unticked. Left unticked, the password and key are forgotten when setup ends. Ticked, the address, username and password or key (with its passphrase) are locked into your vault once the server is set up, and shown under the machine's ⋯ → Details. roamd's website only ever holds them locked. The catch: anyone who can unlock your roamd account can then also log in to that server, so only tick it if you want that.

 tick box off (default) -> setup -> login forgotten
 tick box on            -> setup -> login locked in your vault
                                  -> ⋯ -> Details -> see / copy / forget
- Logging in as root? Tick Allow root / Administrator. roamd then runs as root on that server and starts by itself when it boots, and every browser you approve gets full control of it. It's your choice and your risk. Without the tick, setup stops before installing anything and says so. - If the server can't keep roamd running after you log out without a password, the live output shows one command to run once on the server, and the page reminds you. - It only works for servers on the internet. For a computer on your home or office network, use the next way, from a computer on the same network.

From your own computer

One command on your own computer does the same. Your computer needs roamd installed too (step 2 above).

roamd install-over-ssh me@my-server
 YOUR COMPUTER                         THE SERVER
 -------------                         ----------
 roamd install-over-ssh  -- ssh -->    install roamd
   (asks your ssh                      roamd login --+
    password, if any)                                |
                                       prints a link |
 YOUR BROWSER                                        |
 click Approve, check the  <-------------------------+
 6 digits match           ---- type y in the same window ---->
                                       start roamd as a service
  • It uses your normal ssh, so your keys, ~/.ssh/config and password prompts all work. Extra ssh options go after --, for example roamd install-over-ssh me@my-server -- -p 2222 -i ~/.ssh/work_key.
  • Add --name NAME to choose the name shown in Open sessions (it's the server's own name otherwise).
  • It needs the ssh program (OpenSSH) on your own computer.
  • On a server nobody logs in to, it tries to keep roamd running after you disconnect, and if it can't without a password it prints one more command; run it with ssh -t me@my-server '…'.
  • Logging in as root? Add --allow-root before the server, for example roamd install-over-ssh --allow-root root@my-server. Otherwise it stops before installing anything. With it, every browser you approve gets full control of that server.
  • Windows servers: use the PowerShell install command over ssh by hand for now.

Hostname handover

The canonical origin is https://app.roamd.co.uk. The account database and device registrations are retained. Browser storage and permissions belong to the origin; sign in with the master password, register passkeys again and re-enable push at the new origin. Existing passkeys were scoped to the previous hostname. Reinstall the browser app at the new address if needed.

The previous hostname temporarily proxies machine traffic to the same hub and redirects HTML browser navigations to the canonical site. Updated installers pass the hub explicitly so new registrations use it even with the currently published daemon. The next daemon release rewrites only the previous official HTTPS hub address in saved registrations, preserving the device identity, token and storage protection. Custom hub addresses are unchanged. That daemon change is not released yet.

 browser -> canonical origin -> existing hub and database
 older daemon -> previous hostname -> existing hub
 updated daemon -> canonical origin -> existing hub

Requirements

Supported
Browser Current Chrome, Edge, Firefox, Safari (WebAssembly and WebCrypto random numbers)
Linux x86_64 and aarch64; static musl binaries, no dependencies
macOS Apple Silicon and Intel; not notarised (install from Terminal: Gatekeeper blocks browser downloads of unsigned binaries)
Windows x86_64 (ARM64 via emulation); Windows 10 or later

macOS and Windows builds are cross-compiled and not yet tested on real hardware.

Account

The account is prepared in the browser: email plus master password (minimum 10 characters). The browser derives keys locally and creates the vault. The hub emails a one-time code and creates the account only when it's entered; the browser then shows the recovery key once. See Accounts and the vault.

Installing the agent

curl -fsSL https://app.roamd.co.uk/install.sh | sh        # Linux, macOS
irm https://app.roamd.co.uk/install.ps1 | iex             # Windows PowerShell
 installer
   |
   +-- detect OS + CPU (uname / PROCESSOR_ARCHITECTURE)
   +-- download roamd-<os>-<arch> and SHA256SUMS over HTTPS
   +-- verify SHA-256 against the same site's list; abort on
   |     mismatch or missing entry (no signature check here)
   +-- install to:
   |     root (id -u = 0)       /usr/local/bin       (on the default PATH)
   |     other users            ~/.local/bin
   |     Windows, elevated      %ProgramFiles%\roamd (added to Machine PATH)
   |     Windows, not elevated  %LOCALAPPDATA%\roamd\bin (added to User PATH)
   +-- Unix, folder not on PATH: append one guarded line to
   |     ~/.profile, and ~/.bash_profile, ~/.bash_login, ~/.zprofile
   |     where they exist (once; ROAMD_NO_MODIFY_PATH=1 skips this)
   +-- no tmux on PATH: macOS + a keyboard -> if no brew, ask to run Homebrew's
   |     installer (ROAMD_HOMEBREW_INSTALLER overrides its address), then ask,
   |     `brew install tmux` (answers from the keyboard, default yes); otherwise,
   |     or if that fails, print the install hint as the very last output
   +-- Windows, no tmux on PATH -> if winget is there and a keyboard, ask,
   |     `winget install --id marlocarlo.psmux`; otherwise, or if that fails,
   |     print the install hint as the very last output
   +-- Windows, no PowerShell 7 -> same, `winget install --id Microsoft.PowerShell`
   |     (needed by the psmux session-restore add-ons)
   +-- Windows: a running roamd of the same path is stopped before it is replaced
   +-- every install: print a banner, then run `roamd login --if-needed`
   |     (does nothing if this computer is already registered;
   |     answers come from the keyboard, even when piped into sh;
   |     Windows: if already registered, then `roamd service install` so the
   |     new roamd is running)
   +-- the installer itself creates no service (login does, after `y`)

The added line is case ":$PATH:" in *":DIR:"*) ;; *) export PATH="DIR:$PATH" ;; esac # roamd, so it does nothing when the folder is already on the PATH, and re-running the installer doesn't add it twice (the same approach rustup takes).

Binaries and SHA256SUMS are also linked from Open sessions → Add a machine.

Enrolling a machine

roamd login [--hub URL] [--name NAME] [--no-keyring] [--allow-root] [--config DIR]

On Linux, login seals the host key and device token with a key kept in the desktop keyring (Secret Service API) when one is running and unlocked, and says so; --no-keyring keeps owner-only files. Settings keep the way they were sealed: a later login can move plain files into the keyring, but nothing moves settings already in the keyring back out (to switch, remove the settings folder, or run roamd uninstall, and log in again; see Safety on your machines). --no-keyring has no effect on macOS and Windows, which always use their own secure storage. As root or Administrator, login refuses unless given --allow-root.

Replacing. When a machine asks to join, the approval page is also told which of the account's own devices have the same name (ASCII case-insensitive), with their identity and whether they are online. Approving can name one of them to replace. The hub accepts that only if the device belongs to the same account and has the same name; otherwise it answers "not found" and changes nothing. It then keeps the device id, name, shares, mutes and push settings, stores the new host key and token hash (so the old install's token stops working), clears the last-seen record, and cuts any live tunnel and cached status under the old install. It is logged as machine_added. The new host fingerprint is pinned in your vault; other browsers that had the old one pinned will warn that the machine's identity changed, which is correct.

After a successful y, login runs service install itself (same --allow-root) and ends with Done. This browser can now open a terminal here.; if the service step fails it adds Couldn't set roamd up to run in the background: … with the reason, and says to run roamd service install or roamd run. The registration is kept either way.

 machine (roamd login)        hub                    browser (logged in)
 ---------------------   --------------------   ------------------------
 create host key (Ed25519)
 random secret, commitment
 register name, host key,
   commitment            --> pending approval
                         <-- short code (short-lived)
 print approve link                                   open link
                                                      see name, host fp,
                                                        commitment
                                                      Approve: send browser
                                                        pubkey + own secret
                             device record created <--
 collect result          --> device id, token,
                         <-- browser pubkey, secret
 reveal own secret       -->                    -->   check it matches
                                                        the commitment
 show pairing code                                    show pairing code
   (6 digits from SHA-256 over both fingerprints and both secrets)
 user types y
 save browser key -> authorized_keys
 save device.json (hub, id, token)

The machine trusts nothing until the user confirms on the machine that the pairing codes match. A hub that swapped either key would produce different codes, and because the machine's secret stays hidden until both keys are fixed, it can't search for a key that happens to give the same code (see Connecting to a machine).

The y is typed locally and never sent. After showing the code, the approval page asks the hub for the device list every two seconds; once that machine is online it shows "approved" and moves on to Open sessions. If you typed n, the machine never connects and the page stays on the code.

Codex authentication after installation

The Log in to Codex action runs codex login --device-auth in the terminal of the machine where Codex was installed, on Unix and Windows. It explicitly selects device-code authentication rather than relying on a browser opening on that machine and a localhost OAuth callback. The user approves the displayed code in their own browser; Codex manages the resulting credentials on the remote machine.

roamd login button -> remote Codex device-code flow
  -> browser approval -> Codex stores credentials remotely

Device-code authentication must be enabled in the ChatGPT account's security settings or permitted by the workspace administrator. Browser regressions verify the command and originating terminal; they do not authenticate a real OpenAI account. See OpenAI authentication documentation.

Running and autostart

roamd run [--config DIR] [--allow-root]
roamd service install | uninstall | status [--config DIR] [--allow-root]
OS Service Location
Linux systemd user unit, Restart=on-failure ~/.config/systemd/user/roamd.service
macOS LaunchAgent, KeepAlive on non-zero exit ~/Library/LaunchAgents/uk.co.roamd.roamd.plist
Windows Task Scheduler task at logon and every minute (starts roamd only if it isn't running), least privilege, restart on failure task roamd

service install needs a registered machine (run roamd login first), and prints where the service's messages go: the journal on Linux (journalctl --user -u roamd, without --user for the system unit), a log file under the user's Library/Logs on macOS, and, on Windows, a note that none are kept (the task has no console) with where logins are recorded.

As root or Administrator, service install refuses unless given --allow-root; with it, the service itself runs roamd run --service --allow-root:

OS Service as root / Administrator Location
Linux systemd system unit, User=root, NoNewPrivileges=yes, WantedBy=multi-user.target (starts at boot, no lingering needed) /etc/systemd/system/roamd.service
macOS LaunchDaemon in the system domain, HOME set /Library/LaunchDaemons/uk.co.roamd.roamd.plist, log /var/log/roamd.log
Windows the same logon task at HighestAvailable task roamd

service uninstall and status pick the same kind of service by whether they run as root, without the flag. roamd uninstall (see above) does need --allow-root as root.

The Linux unit stops only roamd's own process (KillMode=process), so tmux sessions started through roamd survive a restart or update of roamd; units installed earlier need roamd service install again. On every OS, service install also turns on bringing tmux sessions back after a restart (tmux-resurrect and tmux-continuum, or psmux-resurrect and psmux-continuum on Windows, which need PowerShell 7; see Safety on your machines) unless it is already on or the user's tmux config already loads those plugins; if fetching the plugins fails, service install still succeeds and says so. roamd tmux-restore [on [--no-screen-text]|off|status] changes it by hand.

Linux user services stop at logout unless lingering is enabled (sudo loginctl enable-linger $USER). With keys in the desktop keyring, roamd run waits (retrying periodically) until the keyring is unlocked, so a service started at boot comes up once the user logs in to the desktop; service install points this out. The service runs roamd run --service, which exits 0 when stopping on purpose (machine removed, unsafe settings) so it isn't restarted in a loop. The macOS and Windows service definitions are unit-tested but not yet run on real systems.

Remote setup over SSH, from the web app

The browser runs the SSH client (the same WebAssembly SSH as for machines) over a WebSocket that the hub splices to the server's TCP port. The hub sees only SSH ciphertext.

 1. hub check: signed in, own page, address and port acceptable
 2. probe connection: SSH key exchange only; the client refuses
    every host key, so the server's fingerprint is learned
    before any authentication is attempted
 3. user compares the fingerprint (or stops: nothing sent)
 4. real connection pinned to that fingerprint; password or
    key authentication; PTY + the shared setup script
 5. the approval code is read from the output and approved
    in the same page (one click)
 6. the page reads "Pairing code: …" from the PTY output,
    compares it with its own, shows the comparison, and
    answers y (equal) or n (different) on the PTY

Why automatic comparison loses nothing: the code the machine prints arrives inside the SSH session whose host key the user pinned in step 3, so the hub can't alter it, while the browser's code is derived from what the hub relayed (fp(H), Nm). That is exactly the two-channel comparison the pairing code exists for; a human reading both numbers off the same page would add nothing. If the server never prints its code in time, the page answers n. When the user runs roamd login themselves, the two codes are on different screens and the user still compares them.

The comparison is paced so it can be watched on any screen size: the answer y/n is only sent once both codes have travelled their routes on the diagram and every digit has been compared. Reduced-motion settings skip the pauses. The panel holds only short step titles; the reasoning lives here in the manual.

Relay restrictions, enforced by the hub on every connection:

  • signed-in account, and only from roamd's own page;
  • the target must resolve to public internet addresses: private, loopback, link-local, shared, reserved and documentation ranges, IPv6 forms that wrap IPv4 addresses, and the hub's own addresses are refused (the vetted address is the one connected to);
  • the SSH port or high ports only;
  • the far end must greet as an SSH server, or the connection is dropped;
  • a limited number of setups per account per hour, and a maximum session length.

Trust: on a first connection there is no known host key, so a hub (or anyone on the path) could present its own key. The fingerprint comparison is the defence; with public-key authentication an impersonator gains nothing reusable, while a password would be exposed to it. The UI says so. Passwords, keys and passphrases are used in memory only and cleared after the attempt, unless the user ticks Save these login details in my vault (default off). Then, once setup finishes and this page approved the machine, the browser seals one vault item per machine holding the address as typed, username, the password or the private key as pasted (still under its passphrase) plus the passphrase, the pinned SSH host fingerprint and the time. It is encrypted like every vault item, so the hub stores only ciphertext. If the machine was approved elsewhere the page can't tie the login to a machine, so nothing is saved and the status says so. Trade-off: whoever unlocks the vault (password, recovery key or passkey) also gets that server login.

 setup finished + box ticked + approved here
   -> seal {address, user, password | key [+ passphrase], fingerprint, time}
   -> vault item for that machine -> hub stores ciphertext
 Details -> unseal in browser -> masked until revealed
 Forget / Remove machine -> item sealed empty at a new revision

Remote setup over SSH, from the command line

roamd install-over-ssh [--hub URL] [--name NAME] [--allow-root]
                       USER@SERVER [-- SSH OPTIONS]

Runs the local OpenSSH client once: ssh [SSH OPTIONS] -t -- USER@SERVER "sh -c '<script>'". The server comes after --, so it can't be read as an ssh option (a server beginning with - is refused anyway), and -t gives roamd login a terminal for the pairing confirmation. The script, run by sh whatever the remote login shell is, stops at the first failure:

  1. without --allow-root (the web app's Allow root / Administrator tick box): if id -u is 0, print This is the root account, … and exit 1, before downloading anything;
  2. fetch install.sh from the hub (a failed download stops here) and run it with downloads from the same hub (the installer verifies the binary's SHA-256 as usual), into /usr/local/bin for root and ~/.local/bin otherwise; later steps run that binary by its full path;
  3. roamd login --no-keyring [--allow-root] --hub HUB [--name NAME]: the normal enrolment, approved in the browser and confirmed with y after comparing pairing codes (this already installs the service, as above);
  4. on systems with loginctl, a best-effort loginctl enable-linger (ignored if it needs a password), so a user service survives logout;
  5. roamd service install [--allow-root], which prints the sudo loginctl enable-linger command if the service would still stop at logout. The last line is roamd setup finished.

Every user-supplied value is single-quoted for the shell, so a name can't add commands. Credentials stay with ssh: roamd never sees the password or key. Remote Windows isn't supported by this command.

Settings folder

OS Default
Linux $XDG_CONFIG_HOME/roamd or ~/.config/roamd
macOS ~/Library/Application Support/roamd
Windows %LOCALAPPDATA%\roamd (local, not roaming)

--config DIR or ROAMD_CONFIG_DIR overrides it.