(remote): check a host's enrolment state from Settings (#222) - #429
Conversation
Each host row gets a Check host button: one read-only ssh reports ssh, the claude CLI and version, tmux, ~/.claude and the login, and hands the command to run on the host for each missing item. Login is read from the exit status of `claude auth status`, whose output is discarded; no credential is read or copied. Closes #222
|
Reviewing |
devsuitup
left a comment
There was a problem hiding this comment.
Adversarial review at 8c2b6e4: 0 blocking. claude auth status runs with stdout, stderr and stdin discarded, and only its exit status is echoed. The version is capped at 64 bytes and checked by a regex. Nothing logs raw ssh output, and the credentials file is never referenced. ENROL_COMMAND is constant, and the alias is validated and must be saved. The parse is strict (a banner, extra lines or ANSI all give "unexpected output"). The renderer uses textContent only. Being fixed: the docs claim a read-only check, but whether auth status refreshes an expired token is unverified. Also being fixed: the message shown on a non-POSIX login shell, two test gaps (stderr not exercised, older-CLI guard mutation survives) and a dead check.
Say what is not known about `claude auth status` (it may refresh a token or touch the network on the host), name a non-POSIX login shell as a cause of an unreadable check, and tell the reader to inspect the install script first. The fake claude now also prints its account details to stderr, and an older CLI without an `auth status` line has its own test, so dropping `2>&1` or the status guard fails a behaviour test. Remove a duplicated size-cap check. Refs #222
devsuitup
left a comment
There was a problem hiding this comment.
Re-review at f81e513 (delta from 8c2b6e4), pushed as f81e513: 0 blocking. The docs say "does not install, log in or write anything itself", and list token refresh by auth status as not verified. The no-facts detail names the POSIX-shell requirement. The fake claude now writes to stderr too: dropping 2>&1 fails a behaviour test. The older-CLI guard has its own test, killed by mutation. The dead check is removed and the install hint says to inspect the script.
What
Each host row in Settings gets a Check host button. One read-only ssh reports a checklist: ssh reachable,
claudepresent (with its version),tmuxor none,~/.claudepresent, account logged in. For every missing or unknown item it shows the command to run, where to run it (on the host, or on this machine for ssh) and a Copy button. This is the Enrolment half of #222 (Launch was #427).It reports state and acquires nothing. The check does not install, log in or write anything itself, and no credential is read, copied, hashed or tested.
The logged-in signal
claude auth statusexits 0 when logged in and 1 when not (checked against CLI 2.1.288 locally, including with an emptyCLAUDE_CONFIG_DIR). It prints the email and organisation, so the check runs it with stdout and stderr discarded and keeps only the exit status. The credentials file is never opened, and its existence is not tested either. Any other result givesunknown — run \claude` on the host once to log in`, never "logged out":claude auth --helpwithout astatusline (older CLI);claudemissing;~/.claude(thenclaude auth statusis not run at all, so the check does not create the host's config files).How
ENROL_COMMANDisPROBE_COMMANDplus fixed text.PROBE_COMMANDandLIST_COMMANDare unchanged and stay pinned. No interpolation: the alias is the ssh operand, checked withisValidAliasand required to be in the savedremoteHosts.parseEnrolis strict: the six lines in order or null. The version is the only free text and is kept only if it looks like a version. 2 KiB output cap, 30 s timeout, samerun()and ssh options, so no new spawn site.checkHost: ssh exit 255, spawn failure or timeout is unreachable; any other failure (a Windows host) is reachable with every other itemunknown.textContentonly. Copy uses the main-process clipboard IPC.Tests
test/remote-transport-enrol.test.js: command pin, negative match for anything credential-like, strict parse,checkHostoutcomes, and a realshwith a stubbedclaude(onlyauth statusis ever run, never without~/.claude).test/remote-enrol.test.js: checklist per state, request guards, one check per host at a time.test/dom-remote-enrol-panel.test.js: statuses, commands and copy, hostile text stays text, button flow.test/main-remote-enrol-wiring.test.js: IPC checks the saved settings, preload, script tag.Mutations (each turned a test red, then restored): auth output not discarded, extra lines accepted, version unchecked, size-cap check removed, exit 255 as reachable, exit 1 not mapped, no
~/.claudeguard, noauthsubcommand guard, undeclared alias accepted, invalid alias accepted, second concurrent check accepted, unknown auth shown as missing, unreachable host leaving items ok, detail and command viainnerHTML, alias untrimmed, main's declared-host check removed. The fakeclaudenow also writes the account details to stderr, and a separate test covers a CLI whoseauth --helphas nostatusline; dropping2>&1and dropping thegrep statusguard each fail a real-shell behaviour test as well as the string pin. The install hint now says to inspect the script first. A duplicate size-cap check incheckHostwas removed. One mutation survived (a re-entry guard on the button, redundant withdisabled), so the guard was removed.Not verified
claude auth statusitself refreshes or rewrites an expired token, or makes network calls, on the host. Switchboard only receives the exit status.sh.claude auth loginorclaude setup-tokenon a machine with no browser. The checklist handsclaude auth login; both subcommands exist in--help, but their headless behaviour was not run.sudo apt install tmuxare assumptions about the host, shown as text only.Linux hosts with a POSIX login shell only: a Windows host, or a fish/csh login shell, is reported as not checked with that cause named (the command is not wrapped in
sh -c, so the probe prefix pin holds). Only tmux is looked for.Closes #222