Security key scripts¶
Six scripts cover the YubiKey-resident SSH workflow end-to-end, plus one helper for re-signing committed history.
gen-sk-ssh¶
Generates two resident keys on the YubiKey — ecdsa-sk and ed25519-sk. Each is created
with:
ssh-keygen \
-t ecdsa-sk \
-O resident \
-O verify-required \
-O no-touch-required \
-O application=ssh:key-touch-required \
-C "<comment>"
After both keys are generated, the script calls get-sk-ssh to load them.
Why two keys?
Different services accept different key types. Shipping both means the same YubiKey works
everywhere without re-running gen-sk-ssh.
get-sk-ssh¶
- If
ssh-add -Lshows no keys, runsssh-add -Kto extract resident keys from the YubiKey into the running agent. - Resolves the user's signing key by trying
git-github-skfirst and falling back togit-forgejo-sk. - Appends the public key to
~/.ssh/.git_allowed_signersin the<email> namespaces="git" <pubkey>format git expects, skipping the write if an identical line is already present.
Run this after gen-sk-ssh, after ssh-agent restarts, or after switching YubiKeys.
git-github-sk¶
Used as git.gpg.ssh.defaultKeyCommand for github.com remotes. The flow:
gh ssh-key list→ all SSH keys associated with the current GitHub user, filtered tosigning-type keys.ssh-add -L→ all public keys currently in the local ssh-agent.- Find the first agent key whose public key blob also appears in the GitHub list.
- Emit it as
key::<line>for git to consume.
Exits non-zero with a useful error if either set is empty or there's no match.
git-forgejo-sk¶
The Forgejo counterpart to git-github-sk — used as
git.gpg.ssh.defaultKeyCommand for Forgejo remotes. The flow:
fj user key list --verbose→ all SSH keys associated with the current Forgejo user, filtered to entries whose key blob starts withsk-ssh-(i.e. security-key-backed keys).ssh-add -L→ all public keys currently in the local ssh-agent. If the agent is empty, triesssh-add -Konce to pull resident keys off the YubiKey.- Find the first agent key whose public key blob also appears in the Forgejo list.
- Emit it as
key::<line>for git to consume.
Exits non-zero with a useful error if either set is empty or there's no match.
gh-switch-user¶
A thin gh wrapper that reads git config github.account from the current repository and, if
the named account is not already the active gh session, calls gh auth switch --user <account>
before forwarding all arguments to command gh.
Configured as the shell-wide gh alias in .zshrc so that every gh invocation in a repo with
github.account set automatically operates under the right identity — no manual switching needed.
Set a per-repo account with:
If github.account is not set (e.g., outside a repo or in a repo without the key), gh runs
unmodified against whichever account is currently active.
git-github-auth¶
git-github-auth # interactive picker if multiple accounts
git-github-auth <login> # target a specific account
Ensures gh is logged in with the scopes this dotfiles setup needs:
gist,notifications,project,repo,user,workflow,read:org,read:public_key,read:ssh_signing_key
Behaviour:
- Not logged in → starts
gh auth login --web --git-protocol https --scopes <set>. - Logged in but wrong account →
gh auth switch --user <login>. - Missing scopes →
gh auth refresh --scopes <set>.
When no <login> argument is provided, the script presents an fzf picker listing every account already authenticated
on this machine, plus a new account entry at the bottom. Selecting new account runs gh auth login so you can
authenticate a GitHub account that has never been used on this machine.
git-resign¶
Re-signs every commit from <target-ref>..HEAD with the currently-configured signing key:
Common use: you authored commits on a machine without the YubiKey loaded, and now you want the chain signed before opening a PR.
History rewrite
This rewrites commit SHAs. Don't run it on a branch that's already published unless you intend to force-push and notify collaborators.
ssh-askpass¶
A bash wrapper that bridges SSH_ASKPASS callbacks from ssh-agent to
pinentry-mac. Wired up via the
launch agent plist.
Key behaviour:
- If the prompt starts with
"Confirm user presence"(FIDO2 user-verification flow), echo a newline immediately — no PIN needed. - Otherwise, parse the SHA256 fingerprint out of the prompt and pass it to pinentry as
SETKEYINFO, so the pinentry GUI can show which key is being unlocked. - Strip pinentry's
Dprefix from the returned PIN before echoing it back.