From 0426ebc97349e766760d40a46f5597e85972dfd1 Mon Sep 17 00:00:00 2001 From: Nic O'Connor Date: Thu, 17 Sep 2026 16:36:20 +0000 Subject: [PATCH] git: authenticate to our Gitea from $GITEA_KEY git never reads GITEA_KEY on its own. With only `credential.helper store`, auth depended on whatever was typed at a first prompt and cached in ~/.git-credentials: it went stale when the token was rotated in the vault, and a box that never pushed had no credential at all, so pushes failed even with a key exported. Add a credential helper scoped to https://git.nic-oconnor.com that hands git $GITEA_KEY as the password (username from GITEA_USER, else the login name). An empty helper entry first clears the inherited `store` for that host, so a rejected token is never cached and a stale one can't win. Other hosts keep `store`. With GITEA_KEY unset it prints nothing and git prompts as before. Idempotent: re-runs leave one copy. Verified: `git credential fill` returns exactly $GITEA_KEY for the host, other hosts are unaffected, ~/.git-credentials stays absent. A dry-run push still fails, because the key currently in the vault is rejected by Gitea (401 on the API too); that is the token, not this wiring. Co-Authored-By: Claude Opus 5 (1M context) --- git/install.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/git/install.sh b/git/install.sh index b3925fb..a242461 100755 --- a/git/install.sh +++ b/git/install.sh @@ -30,4 +30,25 @@ say "fetch.prune = true (stale remote-tracking refs cleaned on every fetch)" git config --global credential.helper store say "credential.helper = store (~/.git-credentials, created on first auth)" -say "Done. On the next HTTPS push/pull to a new host, git prompts once and caches the credential in ~/.git-credentials." +# Our own Gitea authenticates from $GITEA_KEY (exported by secrets/ from the +# vault), not from ~/.git-credentials. +# +# WHY: git never reads GITEA_KEY by itself. With only `store`, the token was +# whatever got typed at the first prompt, copied into ~/.git-credentials, and +# it silently went stale when the vault's key was rotated — or, on a box that +# never pushed, there was no credential at all and the push just failed. Now +# `opc GITEA_KEY` is the only step when the token changes. +# +# The empty helper first clears the inherited `store` for this host, so a +# rejected token is never cached and a stale cached one can never win. Gitea +# accepts the token as the password; the username is informational (GITEA_USER +# overrides it). With GITEA_KEY unset the helper prints nothing and git falls +# back to prompting, as before. +GITEA_URL="https://git.nic-oconnor.com" +git config --global --unset-all "credential.$GITEA_URL.helper" 2>/dev/null || true +git config --global --add "credential.$GITEA_URL.helper" '' +git config --global --add "credential.$GITEA_URL.helper" \ + '!f() { test "$1" = get && test -n "${GITEA_KEY:-}" && printf "username=%s\npassword=%s\n" "${GITEA_USER:-$(id -un)}" "$GITEA_KEY"; }; f' +say "credential helper for $GITEA_URL = \$GITEA_KEY (from secrets.env)" + +say "Done. $GITEA_URL uses \$GITEA_KEY; other HTTPS hosts prompt once and cache in ~/.git-credentials."