···88 ssh=${pkgs.openssh}/bin/ssh
99 yubi_key="''${YUBIKEY_SSH_KEY:-$HOME/.ssh/id_ed25519_sk_rk}"
1010 op_sock="''${ONEPASSWORD_SSH_AUTH_SOCK:-$HOME/.1password/agent.sock}"
1111+ yubi_timeout="''${YUBIKEY_SSH_TIMEOUT:-8}"
1112 err_file="$(${pkgs.coreutils}/bin/mktemp -t yubikey-ssh.XXXXXX)"
1213 trap '${pkgs.coreutils}/bin/rm -f "$err_file"' EXIT
13141414- # First try the local security-key identity without any agent. This
1515- # makes the YubiKey win over whatever identities 1Password exposes.
1616- if [ -r "$yubi_key" ]; then
1717- if "$ssh" \
1515+ try_yubikey() {
1616+ ${pkgs.coreutils}/bin/timeout --foreground "$yubi_timeout" \
1717+ "$ssh" \
1818 -o IdentityAgent=none \
1919 -o IdentitiesOnly=yes \
2020 -o PreferredAuthentications=publickey \
2121 -i "$yubi_key" \
2222- "$@" 2>"$err_file"; then
2222+ "$@"
2323+ }
2424+2525+ # First try the local security-key identity without any agent. This
2626+ # makes the YubiKey win over whatever identities 1Password exposes.
2727+ # If the security-key path hangs, fall back after YUBIKEY_SSH_TIMEOUT
2828+ # seconds rather than blocking git/jj forever.
2929+ if [ -r "$yubi_key" ]; then
3030+ if [ -n "''${YUBIKEY_SSH_DEBUG:-}" ]; then
3131+ try_yubikey "$@"
3232+ else
3333+ try_yubikey "$@" 2>"$err_file"
3434+ fi
3535+ status=$?
3636+3737+ if [ "$status" -eq 0 ]; then
2338 exit 0
2439 fi
25402626- status=$?
2727- if ! ${pkgs.gnugrep}/bin/grep -Eiq 'permission denied|sign_and_send_pubkey|device not found|no such device|agent refused|security key|authenticat' "$err_file"; then
4141+ # 124 = timeout, 137 = timeout killed after TERM was ignored.
4242+ # Treat both as "YubiKey unavailable" and fall back to the agent.
4343+ if [ "$status" -ne 124 ] && [ "$status" -ne 137 ] && ! ${pkgs.gnugrep}/bin/grep -Eiq 'permission denied|sign_and_send_pubkey|device not found|no such device|agent refused|security key|authenticat' "$err_file"; then
2844 ${pkgs.coreutils}/bin/cat "$err_file" >&2
2945 exit "$status"
3046 fi