Safety on your machines¶
roamd runs on your computer, so it's built to be careful there.
+----------------------- your computer -----------------------+
| |
| roamd |
| - runs as YOU, never as the all-powerful admin |
| - only lets in browsers you approved at this computer |
| - writes down every login attempt (access.log) |
| - refuses to start if other users could read its files |
| - its secret files only work on THIS computer |
| - offers a command line, and nothing else |
| |
+-------------------------------------------------------------+
Conversation view¶
Conversation view reads the running agent's messages or the terminal's text through your existing encrypted connection. Only the machine owner can use it. Messages and drafts stay in the browser's memory and are cleared when the session closes or roamd locks; reloading clears them too. Your agent's original records stay on your computer. The hub cannot read them. Visible tool details may contain private information. When Codex shares a background server between terminals, you can choose an open conversation yourself. Its name, folder and ID stay inside the encrypted connection. The choice is kept privately on your computer for this running terminal agent.
your browser <-> encrypted connection <-> your session
read <- agent record or terminal text
Send -> check the same session -> terminal or chosen Codex chat
Sending checks that the same session and agent are still running. roamd does not automatically retry a message after a connection problem. Use the terminal to answer permission questions. Switching views does not start another agent or approve its work. For a chosen Codex chat, Send and Stop go directly to that chat. The caption names the destination. Changing the chat inside Codex does not change your explicit choice in roamd; choose again when needed. The picker does not change Codex's startup settings.
Files opened from terminal output¶
Clicking a file path asks your computer to read it through the encrypted connection already open. The document and your edits are not visible to the hub. The reader is limited to small text files the account running roamd can read, anywhere on the computer, and only the machine's owner can use it. A full path, a parent path or a link can open a file outside the session's folder. Saving requires permission to write the file and its folder; roamd does not ask for administrator access. Each session keeps its own reader and draft in the page's memory. Switching sessions hides the old reader; returning restores it. Closing a tab with edits asks whether to save or discard them. Locking clears every session's draft. The usual Files panel keeps its separate browsing permissions.
browser reader <-> encrypted connection <-> your computer's file
Edit -> Save -> check for changes -> write or keep draft
Reading a document does not run its contents or load its embedded images. Saving needs an explicit click and keeps the file's access permissions on Linux. roamd records who read or edited a file, without its name or contents. A draft lasts only while the unlocked page remains open. Changes made at the very instant of saving can still overlap, so avoid having an agent edit the same file at that moment.
tmux sessions after a restart¶
A restart ends every program. To bring your tmux sessions back, roamd sets up two well-known, free tmux add-ons on your computer: tmux-resurrect, which saves and rebuilds sessions, and tmux-continuum, which saves every few minutes and rebuilds them when tmux starts.
Only a short list of well-known programs is started again (editors and similar tools, and the popular AI coding tools, which carry on their last conversation). Anything else that was running, such as a script or a one-off job, is not re-run: that terminal comes back as a shell in the same folder.
every few minutes after a restart
----------------- ---------------
the add-ons save each session: roamd starts with the computer
windows and panes, and starts tmux; the add-ons
each pane's folder, ---> rebuild your sessions:
the program running in it, same windows and panes,
the text on screen (if on) same folders, programs
started again
- Everything stays on your computer. Nothing about your sessions reaches us.
- Listed programs start again as they were typed. If one of them was interrupted halfway by the restart, it runs again from the start.
- Only the listed programs come back. The popular AI coding command-line tools are started in their own "resume" mode, so they carry on their last conversation in that folder. Anything else comes back as a plain shell in the same folder. Whatever a program only had in memory is gone.
- Screen text is on unless the owner turns it off. It's saved in a normal file on the computer that only your account can read, and it can include passwords.
- Turn it all off in the session list, or with
roamd tmux-restore off. If you already set these add-ons up yourself, roamd leaves your setup alone. - On Windows it works the same way with psmux-resurrect and psmux-continuum, the psmux versions of the two add-ons. They need PowerShell 7, and your sessions come back when you log in again (psmux stops when you log out). An AI tool started through Node (an npm install) looks like "node" to psmux, so it comes back as a shell in the same folder instead of resuming.
Bringing sessions back without a restart¶
If tmux crashes, or you end a session by mistake, you don't have to restart the computer. Open the session list for that machine: sessions in the last save that aren't running are listed under Saved but not running. Press Restore all to bring them back.
open the session list
|
anything saved but not running? no --> nothing extra shown
| yes
v
"Saved but not running: work, build" [Restore all]
|
the add-ons rebuild the missing sessions;
sessions still running are left as they are
|
"Brought back work, build." (the list shows them again)
- Only the machine's owner sees this list and can use the button.
- It brings back everything missing at once, not one chosen session: that is how the add-ons work.
- It uses the last save, so anything changed after that save is missed.
- It shows only when roamd set the add-ons up (not when your own tmux config does).
Running it as the administrator is your choice. Anyone you approve gets the powers of the
account roamd runs as. So roamd stays in your normal account unless you say otherwise: tick
Allow root / Administrator when setting up a server from the website, or add
--allow-root to the command. Then every browser you approve can do anything on that
computer. That's your call, and your risk.
It only offers a terminal. It refuses the extra tricks remote-login programs sometimes allow (tunnelling other connections through, forwarding keys and so on), which attackers like to abuse.
You can see and control who gets in.
roamd keyslists the approved browsers (people you've shared the computer with are marked "guest"; they can log in but can't let anyone else in).roamd keys revoke …removes one, immediately.- An approved browser can swap its own key for a new one (when you Replace browser key), but can't add anyone else. A guest can swap theirs too, so the computer remembers which share each guest's key belongs to and Stop sharing removes whatever key that share holds now, not only the one you first saw.
access.logrecords every attempt, allowed or refused.
Its secrets are sealed to this computer. The computer's ID key and its connection token are locked so that a copy of roamd's folder (in a backup, a synced folder or an email) is useless anywhere else. Windows and Mac use their built-in secure storage. On a Linux desktop, roamd uses your desktop keyring (the password safe that unlocks when you log in, such as GNOME Keyring or KWallet). On a Linux server, which has none, the files are private to your user, the same way SSH keys are kept.
roamd login on Linux
|
+-- desktop keyring running and unlocked?
| yes -> keys locked with a key kept in the keyring
| (roamd then starts only while you're logged in
| to the desktop, when the keyring is unlocked)
| no -> private files, like SSH keys
|
+-- roamd login --no-keyring -> always private files
(for a machine that must come back after a reboot
with nobody logged in)
If the keyring is locked when roamd starts (say, the computer rebooted and nobody has logged in yet), roamd waits and tries again every so often, then carries on by itself once you log in.
Removing it is clean. Remove the computer in Open sessions or run roamd logout: it's
deleted from your account, its token stops working, and roamd stops. (roamd logout also
forgets the approved browsers on that computer. To remove roamd itself, run
roamd uninstall; see Installation.)
A warning when a Mac agent stops progressing¶
roamd can warn the owner when a Mac agent appears busy but makes no visible progress. A permission window on the Mac is one possible cause. The warning is a suggestion to check the Mac, not proof that a window is open.
Mac checks existing session activity
-> encrypted status -> owner's session warning
Work resumes or task ends -> warning clears
This uses the session monitoring roamd already performs. It does not take screenshots, inspect private Mac permission records or request extra access. It cannot grant permission and does not send a push alert for this suspicion. The extra warning is not included in reports to people sharing your machine. Some agents may not be recognised; slow tasks can also trigger the warning. Real permission prompts on a Mac still need testing with this feature.
Process and privilege¶
| Control | Detail |
|---|---|
| Root / Administrator only by choice | login, run, service install and uninstall refuse root (Unix) or elevated (Windows) unless --allow-root is given; the web setup script stops before installing anything on a root login (id -u is 0) unless Allow root / Administrator is ticked, which passes --allow-root on |
| Settings permission check (Linux, macOS) | Refuses to start unless the settings folder belongs to the user and isn't writable by others, and its secret files belong to the user, are readable by no one else and aren't symlinks. New files are created private, written atomically |
| Settings permission check (Windows) | Refuses to start if the settings folder's access list lets in anyone other than the user, SYSTEM and Administrators (the usual state under the user's profile). If Windows' own tools can't be run to look, the check is skipped rather than stopping roamd |
| Autostart | Per-user: systemd user unit (no extra restrictions, so sudo works in your terminals), LaunchAgent, or least-privilege logon task. As root with --allow-root: systemd system unit (User=root, started at boot), LaunchDaemon, or highest-privilege logon task, each running roamd run --service --allow-root. roamd login installs it automatically after approval; see Installation |
SSH server policy¶
| Setting | Value |
|---|---|
| Authentication | Public key only, from the approved list; a few failures end the connection |
| Timeouts and limits | Unauthenticated connections are dropped quickly; sessions and connections are capped; dead connections are detected by keepalives |
| Refused | Port forwarding (both directions), agent forwarding, X11, other subsystems (including SFTP), environment variables |
| Files | roamd's own files subsystem: owners, and guests the owner allowed. Upload names are tidied, uploads never overwrite files or make them executable, and uploads go to one folder unless an owner names another that exists. Downloads (single files, or several as one zip that leaves out links) read only what the machine's user can already read. New folders and renames never replace anything, and delete only moves to the system's Bin. A watch-only guest, or one given only some sessions, is held to the upload folder and can't change anything |
| Offered | An interactive terminal (PTY / Windows ConPTY) running the user's own shell (their SHELL, else sh; PowerShell on Windows), and roamd's own requests: the logged-in key may replace itself with a new public key, only while it is still approved; owners may let guests in or out, change the tmux restore setting, and ask for an update; guests may manage tmux sessions only if the owner allowed it. On Windows the browser's "which sessions are running?" question (also POSIX shell words) is answered by the machine itself with the tmux.exe it found, so the session picker appears; likewise the browser's join command (POSIX shell words) is not run through PowerShell: the machine recognises it (joined_session) and runs tmux new-session -A -s NAME itself as an argument list, with the tmux.exe it finds (psmux: on the PATH, else the WinGet, Scoop, Cargo or Chocolatey folders) |
Secrets at rest¶
host key, device settings
|
+-- Windows: DPAPI (per user), stored in local, non-roaming AppData
+-- macOS: XChaCha20-Poly1305 under a key held in the login Keychain
+-- Linux: XChaCha20-Poly1305 under a key held in the
| desktop keyring (Secret Service API: GNOME Keyring,
| KWallet, KeePassXC), chosen at `roamd login` when
| one is running and unlocked
+-- Linux: otherwise, or with --no-keyring: owner-only files
Each sealed file is bound to its name. A sealed file copied to another machine or user, or edited, fails with a clear "sealed on another machine or user" error. DPAPI is tested under Wine; the macOS Keychain path is not yet run on a real Mac. The Linux keyring path is tested against a real GNOME Keyring (not yet KWallet). Malware running as the same user can usually still reach the keystore: sealing protects copies, not a compromised account.
Linux keyring details. The key is created at roamd login and looked up by settings
folder. roamd never asks the keyring to unlock (that could pop up a password prompt nobody
sees); a locked or missing keyring makes roamd run wait and retry. Settings keep the method
they were sealed with until the next roamd login, so an existing setup (or a service that
starts before anyone logs in) is never moved into the keyring behind its back. Deleting
roamd's keyring entry makes the settings unreadable: remove the settings folder and log in
again.
tmux sessions after a restart, with tmux-resurrect and tmux-continuum¶
roamd doesn't save or restore sessions itself: it sets up the standard plugins tmux-resurrect and tmux-continuum (by Bruno Sutic, MIT licence), and starts tmux after a boot.
turn on (service install, web app, roamd tmux-restore on)
-> fetch both plugins from GitHub at pinned commits,
check each archive's SHA-256, unpack into roamd's folder
-> add one marked block to the user's tmux config (last)
-> apply it to a running tmux at once
roamd run
-> once per boot: start tmux if it isn't running, so
continuum restores the last save
-> every few minutes: run continuum's save check (it
otherwise saves only while a client is attached)
- Plugin settings: only a short list of known programs is brought back, never everything that
was running. resurrect's own list of programs it knows how to restart is kept, and AI coding agents are restarted with their documented
resume options:
codex resume --last,claude --continue,gemini --resume,aider --restore-chat-history(keeping its other options),opencode --continue,copilot --continue; each matched both as an installed program and when run through its interpreter (npm and pip installs). Pane contents on or off by the owner's choice; continuum auto-restore on, saving every few minutes. - What's kept, and where: resurrect's save files and pane-content archives, in its usual folder in the user's home, readable only by the user and not encrypted. Nothing is sent to the hub.
- The config block is marked, rewritten in place when the choice changes, and removed exactly on "off". A config that already loads either plugin (for instance through TPM) is left alone, and the web app says so. The plugins live in roamd's settings folder, so they never clash with a user's own plugin folder.
- Once per boot: roamd records the boot it last handled (Linux: the kernel's boot id; macOS: the boot time), so installing or updating roamd never starts tmux or brings back sessions that were closed on purpose.
- Who changes it: anyone who can log in can see the setting; only an owner's key can change it, over the end-to-end SSH session.
- roamd restarts: the Linux service stops only roamd's own process, so tmux and its
programs keep running when roamd is restarted or updated (services installed before this
need
roamd service installagain). - Limits: only the listed programs are re-run (including one a restart interrupted); everything else comes back as a shell in the same folder; their memory and
environment variables set by hand aren't restored; continuum skips automatic restore when
the user runs more than one tmux server; changes since the last save are missed; not yet run on a
real Mac; restoring needs
bashandtar.
Restore all, on request¶
picker opens (owner, restoring on)
-> "saved": the machine reads resurrect's last save
(tmux: the "pane" lines' session names; psmux: the JSON
save that "last" points to) and answers the names not in
tmux's own list of sessions
Restore all
-> "restore saved" (owners only), on the same SSH subsystem
tmux running? yes -> run resurrect's restore script
no -> start tmux (psmux: a throwaway session);
if continuum hasn't begun restoring within
a few seconds, run resurrect's restore itself
-> wait until every saved session is running (or give up after a
while, naming the ones that didn't come back)
-> the page lists the sessions again
- Both resurrect ports skip a pane or session that already exists, so running restore with some sessions still up only adds the missing ones; nothing is doubled.
- A guest's request is refused, and an older roamd answers "unknown request", so the page shows nothing.
- Where the save is: roamd's chosen folder if set, else the plugin's own default
(
~/.tmux/resurrectif it exists, else$XDG_DATA_HOME/tmux/resurrect; psmux:~/.psmux/resurrect). A@resurrect-dirset in your own config isn't read.
The same on Windows, with psmux-resurrect and psmux-continuum¶
On Windows roamd sets up the psmux ports of the two plugins (psmux-plugins, MIT licence): the same idea, with these differences.
turn on (service install, web app, roamd tmux-restore on)
-> fetch the psmux-plugins archive at a pinned commit, check its
SHA-256, unpack both plugins into ~/.psmux/plugins
-> write a small strategy script per program into ~/.psmux/strategies
-> add one marked block to psmux's config (last)
roamd run
-> a heartbeat file every minute
-> after a fresh logon (a gap in the heartbeat) with a save to
restore: start psmux with a throwaway session; continuum's
start hook restores, then roamd removes the throwaway
-> every few minutes: run resurrect's save
- Needs PowerShell 7 (
pwsh): the ports are PowerShell scripts. Turning on without it fails with thewingetcommand that installs it, and changes nothing. roamd looks on the PATH and in Program Files (PowerShell\7and7-preview),%LOCALAPPDATA%(Programs\PowerShell\7, the Store'sWindowsApps),.dotnet\toolsandscoop\shims; the installer looks in the same places. - Config file: the first that exists of
~/.psmux.conf,~/.psmuxrc,~/.tmux.confand~/.config/psmux/psmux.conf(the order psmux reads them), else a new~/.psmux.conf, so roamd's block never hides a.tmux.confpsmux was already using. The block loads each plugin's ownplugin.confwithsource-file, by absolute path. - Programs: psmux saves only a program's name, so roamd writes a strategy script for
each AI tool that types its resume command (
claude --continue,codex resume --lastand the others listed above), and for the programs the port would otherwise re-run by name (python,python3,node,npm,ssh,wsl) a script that types only a comment. Roamd's scripts are named<program>_roamd.ps1and are removed on "off"; yours are never touched. A tool started through Node shows asnodeand isn't resumed. - Your own setup: a config that names either psmux plugin, or a plugin folder PPM unpacked, is left alone, as on Linux.
- Once per logon: Windows fast start-up keeps its boot time across a shutdown, so roamd keeps a heartbeat instead. A gap of more than three minutes means roamd wasn't running, so psmux wasn't either. A restart of roamd for an update leaves only seconds, so it never starts psmux or brings back sessions closed on purpose.
- Limits: not yet run on a real Windows PC; changes since the last save are missed;
restoring needs
tar(built into Windows 10 and later).
Audit and control¶
access.log: UTC timestamp, event (login accepted or refused, connection closed or refused, key replaced, guest added or removed, file received or fetched), key fingerprint(s) or the reason. Owner-only, rotated automatically.- The approved-keys list is re-read on every attempt, so
roamd keys revokeapplies to a running agent immediately, including new terminals or commands asked for on a connection that logged in earlier. A damaged list admits nobody. Changes to the list are locked against each other, so a key replacement can't undo a revoke made at the same moment. - A command can also run on a terminal (how a browser joins a tmux session), through the login shell, and ends when the command does.
- Commands run without a terminal pass their output on as it comes and stop when their connection ends; a shell ends with its session, however that ends.
- Removing a machine (from the web app or
roamd logout) deletes it on the hub. Its token stops working androamd runexits.
Known gap: revoking an owner's key doesn't yet close terminals already open (it stops new ones at once). Removing a machine from the web app ends the terminals open on it, and a guest's open terminals and commands are cut as soon as their key is removed or their time ends.
Live conversation access¶
Conversation reads and input require a currently authorised owner key, checked again after the request arrives. The daemon binds requests to the exact tmux pane, agent process start and linked conversation. Hook associations stay private on the machine; Claude process registration and Linux Codex transcript descriptors can also identify the current record. The view never chooses by folder or recency. Hooks store only the association, not another transcript or the user's message. An explicit Codex selection reads loaded root-thread metadata through the existing local app-server connection, without starting or resuming threads. The association records the selected thread and terminal process start identity. Selected input uses that thread's protocol methods rather than typing into a potentially different TUI conversation. Active-turn steering and interruption include the expected turn identity. Closed threads are rejected, and approvals are not answered by this client. Codex hooks from a detached shared server are ignored because its inherited terminal environment can identify a different client. Automatic hook linking checks process ancestry. Native Mac and Windows runtime verification remains outstanding.
owner browser -> encrypted SSH -> daemon checks authority
read -> exact process -> transcript or tmux capture
input -> check identity -> pane or selected Codex thread
Transcript reads are bounded and return visible message/tool records, excluding hidden instructions and reasoning. Unlike the History archive they do not mask secrets in visible content. The browser renders safe Markdown and fetches no embedded images. Text and drafts remain in page memory and are cleared on close or lock. Input travels in the encrypted channel and into tmux through standard input, never process arguments. The daemon checks identity before paste and again before submitting Enter. An exit racing input delivery remains possible; terminal input is not an atomic agent API. Input is never automatically retried, and approval prompts stay in the terminal. The hub has neither the transcript nor credentials that decrypt the connection. Linux is tested; physical macOS and Windows verification remains outstanding.
Project text access¶
authenticated owner's browser -> existing SSH file channel
-> resolve path -> OS permissions -> bounded UTF-8 read
-> explicit edit + expected revision -> checked replacement
Project document operations check the owner's authorisation independently of general file transfers. The named tmux session supplies the base directory for relative paths. Absolute paths, parent paths and symbolic links can reach other directories under the daemon account's OS permissions. The daemon resolves the target, then opens its parent directory for reads and atomic saves; saving through a link updates the target and preserves the link. There is no session-directory access boundary or privilege elevation. Saves check write access to the existing file as well as requiring write access to its parent directory. Save authorisation is checked again after receiving the draft. Reads are bounded and regular-file-only; edits also reject unsupported ownership and hard-link arrangements on Unix.
A revision combines SHA-256 of the text with file metadata. Saves compare it before and after preparing a replacement. A daemon-wide lock orders its own saves, but external writers can still race the final replacement. Unix owner, group and mode bits are preserved; custom ACLs and extended attributes are not guaranteed to survive. The access log records the reader/editor key and action, not paths or contents. Browser rendering ignores raw HTML and embedded images and retains the strict CSP. Reader state and asynchronous results are isolated by originating session tab; only the active tab's reader is displayed. Unsaved edits remain in page memory when hidden, never browser storage. User tab closes protect drafts even when ordinary confirmation is disabled. Closing a tab clears its reader; locking clears all readers and prevents late file responses from restoring them. The hub receives encrypted SSH traffic, not document plaintext. Linux is tested; native macOS and Windows verification remains outstanding.
Suspected Mac permission stalls¶
The Mac daemon combines recognised agent activity with a local fingerprint of meaningful terminal output. Busy-footer timers and spinners are excluded from progress. The resulting stall marker uses the existing signed, end-to-end encrypted owner status channel. Guest reports omit it. It adds no screenshots or permission-specific paths to the report, and no readable status for the hub.
Activity + screen fingerprint -> local suspected stall
-> signed, encrypted owner report -> banner and tab marker
Progress / task end -> clear marker in next report
The detector does not read the TCC database, inspect system dialogs, ask for Accessibility or Screen Recording, or attempt to approve privacy requests. It cannot reliably distinguish a pending privacy dialog from a long tool call. Known terminal input requests retain the existing waiting state. No extra push is generated for a suspected stall. Tests cover controlled activity, report boundaries and browser behaviour; native macOS prompt validation is outstanding.