Skip to content

Say why a seed read failed, and try it once more - #24

Merged
lfnothias merged 4 commits into
mainfrom
fix/totp-read-diagnostics
Oct 8, 2026
Merged

lfnothias merged 4 commits into
mainfrom
fix/totp-read-diagnostics

Conversation

@lfnothias

Copy link
Copy Markdown
Contributor

Stacked on #23 (base chore/release-v0.1.0), because the entry belongs in its CHANGELOG.

A failed seed read sent the backend's stderr to /dev/null. open, doctor and status then reported "no seed — run store-seed", including when the seed was stored and the keychain had refused a single request. That points the user towards re-enrolling a factor that is fine, and leaves an intermittent refusal with nothing to diagnose it from.

Changes, in lib/totp.sh:

  • Each failed read appends one line to HS_TOTP_ERROR_LOG. The default is $HS_CONFIG_DIR/<profile>.totp-errors.log, created 0600. The line records the time, backend, attempt, exit status, the backend's message and, for the keychain, launchctl managername. Only stderr is written. The seed is handed on with printf, a builtin, so as before it reaches no argv, no environment and no file.
  • The hint printed by open, doctor and status repeats the latest reason. The store-seed advice remains as the fallback.
  • A failed read is tried once more after HS_TOTP_READ_RETRY_DELAY seconds (default 2). There is no retry after a cancelled prompt, or for a missing seed file.

The two new keys are documented in config.example. SECURITY.md, docs/2fa-enrollment.md and CHANGELOG.md are updated.

Tests: 13 new assertions, 207 in total, with security shadowed throughout. Against the previous lib/totp.sh, 11 of the 13 fail; the other two assert that nothing is written, which also held before. shellcheck, run as in CI, is clean.

Generated with Claude Code

lfnothias and others added 4 commits August 21, 2026 10:48
Nothing here was ever tagged, so anyone who cloned since 2026-07-29 holds a copy
with defects that are mostly silent — a watch that reports running jobs as
finished, a precedence rule that discards every documented override, a submit
that fails on any current OpenSSH. The changelog leads with what CHANGED rather
than what was added, because that is the part that can surprise someone
upgrading: sbatch arguments are quoted now, a remote script path is literal, and
watch has a third exit status.

The examples directory had only the hardest case, a cluster behind a full-tunnel
VPN that also enforces TOTP. Most people start with neither, and README solicits
worked profiles from contributors — so the simple shape should be there to copy.
A first tag makes version a question with an answer, and CHANGELOG.md opens with
behaviour changes — so a caller has to be able to tell which side of them it is on.
Nothing in the tool could say.

`version` is dispatched before the profile loads, like `init`: a setup that does not
load is exactly when someone needs to report which copy they are running. `doctor`
opens with the same line, since that output is what gets pasted into an issue.

A test holds the string equal to the newest heading in CHANGELOG.md, so a tag cannot
drift from what the tool reports. Verified by mutation: drifting the string fails
three assertions, dropping the doctor line fails one, and moving the dispatch after
the loader fails the two that cover a broken profile.

Also corrects two claims in the changelog itself — the repository's public date, which
was off by a timezone, and the assertion count.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "Known limitations" section said no part of this release had been exercised
against a live SLURM controller. That is no longer true: one end-to-end run —
open, render, submit, queue, watch to completion, fetch, cancel, close — was
made against a TOTP-gated cluster behind a VPN, and behaved as documented.

Stated as one site and one SLURM version, which is what it is, rather than as a
compatibility claim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A failed read sent the backend's stderr to /dev/null, so open, doctor and status
all said "no seed — run store-seed" while the seed sat in the keychain, refused
for one request only. The backend's own message, its exit status and, for the
keychain, the macOS session the read ran in are now appended to
HS_TOTP_ERROR_LOG, and the hint repeats the latest reason. The seed itself is
never written anywhere.

The read is tried once more after HS_TOTP_READ_RETRY_DELAY seconds, which
absorbs a refusal that clears by itself; not after a cancelled prompt, and not
for a missing seed file.

Thirteen new assertions, with `security` shadowed throughout; they fail on the
previous code. Suite: 207 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lfnothias
lfnothias changed the base branch from chore/release-v0.1.0 to main October 8, 2026 07:08
@lfnothias
lfnothias merged commit 3d356bb into main Oct 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant