Lane: hosts (w29:p4)
Owned paths: docs/host-setup.md, scripts/**, docs/lanes/hosts.md. For pairing, that's
scripts/herdr-pair.py, scripts/herdr-pair.test.py, scripts/qrcodegen.py and the pairing
section of host-setup.
Stage: done. The attach commands are in host-setup and went to
transport (w29:p2) for HerdrCommand.attach(platform:session:) on 2026-09-21.
Verdicts
Each check ran ssh -tt (pty-req + exec, TERM=xterm-256color) with the exact attach string.
macOS: works. Tested on this Mac (herdr 0.9.1 client, server 0.9.0), over 127.0.0.1 and
the tailnet address 100.103.220.58:
- Bare
herdrisn't found in an SSH exec, because PATH is~/.cargo/bin:/usr/bin:/bin:/usr/sbin:/sbin.exec "$SHELL" -lc '…'resolves/opt/homebrew/bin/herdr. - Throwaway session
hosts-probe: renders the desktop layout. Typing works, and the remote pane seesTERM=xterm-256colorandHERDR_SESSION=hosts-probe.ctrl+b qexits SSH with 0 and the session stays running. - Reattaching at
stty cols 50shows the mobile layout (header with tab and switch, full-width pane, 49 inner columns) with the earlier output intact. - Window-change over SSH reflows live. The layout area goes 94×39 at 120 cols (desktop), 50×38 at 50 cols (mobile), then 154×49 at 180 cols.
defaultattached via bareherdrat the live client's size and rendered the running workspaces. After detach the panes were back at 296×79.
Windows (supedupsilly, herdr 0.9.0, OpenSSH_for_Windows 9.5p2): works.
- sshd's
DefaultShellis Windows PowerShell 5.1. The profile loads on every exec, andSet-StrictModeis on through a dot-sourced script. The user PATH carries herdr, so bareherdrresolves. - Throwaway
hosts-probeinside ConPTY: renders the desktop layout. Typing works (TERM=xterm-256color, 269 inner columns).ctrl+b qexits with 0. - The server started over SSH (pid 9444, Windows session 0) was still running after the SSH session closed. Reattaches showed the earlier output.
- 50 cols gives the mobile layout (50×38). Resizing goes to desktop at 120 cols (94×39) and back.
cmd /c herdr session attach hosts-probealso attaches, which covers hosts whose default shell is cmd.exe.default(console session 1, one idle Claude agent inw1:p1) attached via bareherdrat its own size (area 134×79 before and after). The agent stayed idle.- A missing binary under a PTY exits 0 after printing
The term 'herdr-missing' is not recognized…(exit 1 without a PTY). On macOS it exits 127.
Both hosts-probe sessions are stopped and deleted. No other session was touched.
authorize-key.sh: checked with local on this Mac, and on the PC with platform
auto-detection. There, an admin with the Match Group administrators block commented out lands
in %USERPROFILE%\.ssh\authorized_keys as UTF-8 without a BOM, with ACL
Administrators/SYSTEM/volpe. A second run reports present. scripts/authorize-key.test.sh
covers idempotency, perms, rejection of options and multi-line input, and SSH-failure exit status.
Mutation checks confirmed it fails on the earlier pipe and the earlier validation. Neighbor w29:p7
reviewed both fixes.
QR pairing helper (ADR 0002)
Stage: done. scripts/herdr-pair.py (stdlib, Python 3.9+) plus vendored Nayuki qrcodegen.py
(MIT header kept, upstream sha256 9f4ed1dd…c8ef). Usage and guarantees are in
host-setup › Pair with a QR code.
Evidence, 2026-09-21:
python3 scripts/herdr-pair.test.py --remote-windows volpe@supedupsilly 'C:\Users\volpe\herdr-pair-test'on this Mac, with/usr/bin/python33.9: all 42 checks pass (after the review fixes below).- The local set covers RFC 3986 URL escaping, approve, claim-once (a second enroll gets
USED) bound to the claiming key, deny, expiry, a late approval (EXPIRED, no key), a foreground deadline re-check, and invalid input. It also covers refusing a piped stdin outside test mode, discarding aytyped before the prompt (pty), and SIGINT/SIGTERM cleanup. - The same probes run against a private loopback sshd and against supedupsilly's real sshd
over the tailnet (Windows PowerShell default shell). The one-time key rebuilt from the QR
seed gets
INVALIDforexec whoami;-tgives "PTY allocation request failed"; the sftp subsystem runs the enroll;-Ris refused and-Lgives "administratively prohibited". Enroll then printsOK, the enrolled key logs in, the one-time key is rejected afterwards, and a denial leaves no key. - Both PC key files end byte-for-byte as they started.
- The local set covers RFC 3986 URL escaping, approve, claim-once (a second enroll gets
- On the PC itself (Python 3.11), the local set passes too (21 checks; the pty and SIGTERM ones are unix-only; Windows exit for Ctrl+Break: 149).
- Mutation checks: making the claim non-exclusive, dropping the deadline re-check, and dropping
the cleanup in
finallyeach fail the suite. - The QR renderer's half-block output, turned back into modules and decoded with CoreImage, returns the exact 345-char URL. It renders at 73×37 cells.
- The app lane paired the iPhone simulator with this Mac end to end through
simctl openurl. That run found the+-for-space URL bug, now fixed withquote_via=urllib.parse.quoteand covered by a check.
Incident: the first --remote-windows run's cleanup rewrote every candidate keys file, including
the PC's unused C:\ProgramData\ssh\administrators_authorized_keys. Its key text was unchanged,
but 2 CRLFs became LF. I restored it byte-for-byte from the 2026-09-16 shadow copy (sha256
95A5B128…FEE6A, ACL unchanged). The cleanup now touches only files that contain the test key
and keeps their line endings. The helper itself only ever wrote the per-user file.
Review (w29:p5): accept-with-fixes, both fixed.
- A second enroll for a claimed id now answers
USED(exit 5) instead ofDENIED. That lets the phone tell a leaked-code race apart from a human "no". The lead signed off in ADR 0002, and HerdrKit maps it (25ea9ad). - Approval needs a TTY on stdin outside
HERDR_PAIR_TEST, and typed-ahead input is flushed before the prompt (tcflushon unix, anmsvcrtdrain on Windows). A scriptedecho y |or a stray keypress can no longer approve an unseen device.
Protocol notes (ADR 0002 as implemented):
- Outcome
INVALID(exit 2) covers a malformed key or name. The enroll's approval wait ends at the earlier of 120 s and the code's expiry. - The enroll accepts
ssh-ed25519 <b64>with a trailing comment and ignores the comment. - Under Windows' PowerShell default shell, sshd reports exits 3, 4 and 5 as 1. HerdrKit decides on the token (transport, 88180ed).
- If the helper is killed outright (SIGKILL, or the Windows SSH session that launched it
dropping), the restricted line stays until its
expiry-time. That's the documented backstop.
Follow-ups
- herdr (upstream):
WINDOWS_POWERSHELL_SHELL_INTEGRATION_COMMAND(src/pane.rs) reads$global:__HerdrOriginalPromptbefore setting it, so it throws underSet-StrictModeand the cwd integration never installs. Guard it withTest-Path variable:global:__HerdrOriginalPrompt. supedupsilly hits this because its profile dot-sourcesopencode-private.ps1, whose first line isSet-StrictMode -Version Latest. - herdr upstream: Windows admin SSH PATH doesn't follow updates. On supedupsilly the user
PATH names
C:\Users\volpe\.herdr\packages\standalone\releases\0.9.0-x86_64-pc-windows-msvc. The installer's%LOCALAPPDATA%\Programs\Herdr\bin(not on PATH) is not a shim. It is a junction to that same release folder, likestandalone\current, created 2026-09-09 by the installer at medium integrity.- volpe's SSH sessions are elevated (
High Mandatory Level,IsInRole(Administrator)True). Windows refuses to let them follow the junction:[IO.Directory]::GetFilesthrows "The path cannot be traversed because it contains an untrusted mount point", andTest-Path …\bin\herdr.exeis False. So prepending it would add a dead entry, not an update-proof one. The user PATH was left unchanged (checked 2026-09-21). herdr updateruns the bundledinstall.ps1, which retargets the junctions and keeps three releases (-Retain 3). The versioned entry therefore survives two updates, but after the first one SSH runs the old client against the new server.- Upstream fix: give elevated sessions a launcher that isn't a medium-integrity junction, for
example a real
herdr.execopy or a hardlink in the bin folder. Until then, repoint the PATH entry after each update on the PC.
- volpe's SSH sessions are elevated (
- transport/app: Windows PTY sessions report exit status 0 even when the command fails, so treat an early channel close as a failure and show the output.
