Validate agent SSH key: fix false-pass auth check, add per-org SSO, wire into claudeconfig#12
Merged
Merged
Conversation
…gent-ssh-key check-agent-ssh-key step 7 omitted -F /dev/null, so ~/.ssh/config's additive `Host *` IdentityFile leaked the human laptop key as a candidate (IdentitiesOnly=yes does not drop config-supplied identities). ssh offered the laptop key first and the server accepted it; since both keys live on the same GitHub account the greeting was identical, so the check passed via the wrong key and would green-light even a broken or unregistered agent key. Add -F /dev/null plus a fingerprint assertion against the -v 'Server accepts key' line so step 7 genuinely exercises this key. Add step 8: per-org SAML SSO authorization probe, gated on the work role. A key can authenticate to github.com yet still be rejected for org-owned repos until authorized for that org's SAML SSO (browser-only, not exposed via gh/API). Probe a canary repo per org with git ls-remote and detect the 'enabled or enforced SAML SSO' error. Found when the work agent key authed fine but was unauthorized for guideline-app (Gusto was already authorized). Non-work roles get an empty org list and skip the block. Bean: dotfiles-w4tk Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
setup-agent-ssh-key never mentioned the work-role SAML SSO authorization step (github.com/settings/keys -> Configure SSO) -- the exact gap that let an un-authorized agent key slip through. Add it as a work-role-only step, and replace the manual 'make a commit / check the badge' verification with a pointer to check-agent-ssh-key, which now validates files, Keychain, GitHub registration, auth, and per-org SSO in one shot. Bean: dotfiles-w4tk Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generalize claudeconfig.sh from apply-only to apply-and-validate: after the existing config phases, a validate phase delegates to bin/check-agent-ssh-key for the active role and fails loud if the key is wrong. Apply runs first, so config is never left half-written; only the exit code reflects validation. The agent email is read from the role's gitconfig include (home/.gitconfig.d/claude-agent-<role> user.email), so there's one source of truth and no per-role email mapping to drift. Roles with no agent include (e.g. base) are a no-op. --skip-ssh-check (or SKIP_SSH_CHECK=1) bypasses only the validation, leaving apply intact -- needed offline and on fresh machines, where claudeconfig runs before the key is registered/SSO-authorized on GitHub. setup-agent-ssh-key step 4 now uses it. Bean: dotfiles-cr2m Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the decision to validate the active role's agent SSH identity from claudeconfig.sh (fail-loud, with --skip-ssh-check escape hatch), and reconcile the hard-fail stance with ADR 0036's warn-not-fail for missing role files: a missing role file degrades to a usable config, a broken agent identity is external state that fails confusingly at runtime. Captures the two failure modes that motivated it (wrong-key false pass, missing per-org SAML SSO authorization). Bean: dotfiles-cr2m Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
.beans/ files are auto-generated by the beans tracker and routinely contain markdown-emphasis-like text (*, _) that prettier rewrites, breaking format:check on every push. Add .beans/ to the prettier ignore list so they're never linted. Also prettier --write two files that had drifted on main (.claude/settings.json, doc/adr/0031), which is what turned main's CI red since 2026-06-13. This greens both PR #12 and main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
check-agent-ssh-key's SSH auth check was a false pass: without-F /dev/nullit authenticated via the human laptop key (same GitHub account, so theHi <user>\!greeting was identical) and would have green-lit a broken or unregistered agent key. Now it forces the agent key and asserts the accepted fingerprint matches.claudeconfig.shnow validates the active role's agent SSH identity after applying config, fail-loud, with--skip-ssh-checkfor offline / fresh-setup. Rationale and the contrast with ADR 0036's warn-not-fail are in ADR 0037.setup-agent-ssh-keynow spells out the SSO-authorization step (the bit that's easy to forget) and points at the verifier instead of a manual badge check.Test plan
bin/check-agent-ssh-key work --email josh.nichols+agent@gusto.comreports both orgs SSO-authorized and "works with this key".ssh -vconfirmed: with-F /dev/nullthe server accepts the agent key; without it, the laptop key.claudeconfig.sh --help, bad-arg (exit 2), email derivation, base no-op, and--skip-ssh-checkearly-return all verified. Full apply not run here (it rewrites live~/.claude/settings.json).Note
With validation live, your next
claudeconfig.sh(work role) fails untilgh auth refresh -h github.com -s user:email, the real gap the check flags today. That's fail-loud working as intended;--skip-ssh-checkbypasses it.