git credential para coding agents: helpers, no tokens en el prompt
Resumen
git credential expone el sistema de credenciales de Git a scripts con tres acciones: fill, approve y reject. Los helpers (cache, store, osxkeychain, manager) guardan el secreto fuera del prompt y fuera de la URL. Cero usuario y token pegados en el remote, cero helper --global impuesto por un agente y cero volcar la salida de fill al contexto del modelo. Git 2.50.1.

Qué resuelve
Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.
git credential expone a scripts la misma interfaz que Git usa por dentro para pedir, guardar y olvidar usuario y contraseña, sin mover HEAD, sin tocar el índice y sin tocar el working tree. El man (git-credential(1); git-scm.com/docs/git-credential; man local Git 2.50.1, Apple Git-155): Retrieve and store user credentials. SYNOPSIS: git credential (fill | approve | reject | capability). Lee una descripción por stdin (protocol=https, host=github.com, path=...) y, según la acción, la completa, la guarda o la borra.
Es el eslabón que casi todos los agentes se saltan: el push falla con 401, el agente reintenta pegando el token en la URL del remote o pidiéndolo por chat, y el secreto termina en el prompt, en el log y en .git/config. Con git credential el flujo correcto existe y es corto: fill pide al helper, approve guarda tras un uso exitoso, reject olvida tras un 401. El secreto vive en el helper, nunca en el contexto del modelo.
No es config. config solo elige qué helper usar (credential.helper) y con qué alcance (--local vs --global); credential es el que mueve el secreto. Configurar el helper no guarda nada; invocar fill sin helper configurado solo pregunta por terminal. No es remote. remote lista y nombra URLs; si la URL trae https://usuario:token@host, ese token ya quedó grabado en .git/config en texto plano y cualquier cat posterior lo expone. La URL debe ir limpia (https://github.com/org/repo.git) y la auth la resuelve el helper. No es secretos. Esa guía cubre dónde vive el secreto (env, Actions, fuera del prompt); esta cubre el mecanismo Git que lo entrega al push sin pasarlo por tus manos. Y no es OIDC: OIDC elimina el secreto estático en CI con id-token; credential lo gestiona donde un humano o un agente local sí necesita un token persistente. En todos rige la misma higiene: cero volcar credenciales al contexto del LLM.
Contrato: printf 'protocol=https\nhost=github.com\n' | git credential fill. Exit 0 con username= y password= completos = helper resolvió. Tras push exitoso, approve; tras 401, reject. Cero token en la URL del remote. Cero store en disco compartido. Cero salida de fill pegada en el chat del agente.
Las tres acciones y cuándo las invoca un agente
fill intenta completar username y password leyendo config, consultando cada helper configurado y, como último recurso, preguntando por terminal. Un agente sin TTY nunca debe llegar a ese último paso: si fill pide interacción, la configuración está incompleta y hay que detenerse, no improvisar. approve guarda la credencial en los helpers (se invoca solo después de un uso exitoso). reject la borra (se invoca cuando el servidor la rechaza). Git mismo hace este ciclo en cada push; el agente solo necesita replicarlo cuando gestiona auth por su cuenta.
# fill: completa desde helpers, sin interacción si todo está bien
printf 'protocol=https\nhost=github.com\n' | git credential fill
# approve: solo tras un push que sí funcionó
printf 'protocol=https\nhost=github.com\nusername=x\npassword=ghp_...\n' | git credential approve
# reject: el token murió (401) → olvidar antes de rotar
printf 'protocol=https\nhost=github.com\nusername=x\npassword=ghp_...\n' | git credential reject

El detalle que muerde: approve y reject reciben la credencial completa por stdin. Eso significa que el agente la tiene en memoria en ese momento. Úsala y suéltala: nunca la imprimas, nunca la guardes en una variable de entorno persistente, nunca la escribas en un archivo de log. El ciclo entero debe ocurrir dentro del mismo paso efímero que hace el push.
Qué helper elegir (y qué prohibir)
Git no trae almacenamiento propio; delega en helpers. La elección se guarda con git config credential.helper, y para un agente rige la misma regla que en config: solo --local, cero --global. Un agente que escribe credential.helper store en --global cambia el comportamiento de todos los repos del usuario en esa máquina.
| Helper | Dónde guarda | Cuándo sí | Veredicto para agentes |
|---|---|---|---|
cache | Memoria, con timeout (--timeout 3600) | Sesión de trabajo acotada, runner efímero | Default recomendado: el secreto muere con la sesión |
osxkeychain | Llavero de macOS | Laptop del humano, helper del sistema (viene por defecto en Apple Git) | Respetarlo, nunca reconfigurarlo ni extraer de él |
manager (GCM) | Almacén del SO (Windows/macOS/Linux) | Estación del humano con Git Credential Manager | Igual que osxkeychain: usar, no tocar |
store | ~/.git-credentials en texto plano | Casi nunca | Prohibido para agentes: un cat lo lee todo, sin cifrado ni timeout |
cache --timeout corto | Memoria, minutos | Agente que hace 2–3 pushes y termina | Mejor aún: 300–900 s en vez de la hora por defecto |
La matriz se lee así: en la laptop del humano ya hay un helper del sistema (en macOS, osxkeychain viene configurado de fábrica) y el agente no debe ni cambiarlo ni leerlo directamente; en un runner o contenedor efímero, cache con timeout corto es el techo correcto porque el secreto desaparece solo. store no tiene ningún caso de uso legítimo operado por un agente: persiste para siempre, sin cifrado, en un archivo que cualquier otro proceso puede leer.
Los tres pecados que sí cometen los agentes
Primero, el token en la URL del remote. git remote set-url origin https://x:[email protected]/org/repo.git funciona, y por eso los agentes lo hacen: el push pasa. Pero el secreto queda en .git/config en texto plano, aparece en cada git remote -v, y cualquier clon, backup o cat del repo lo propaga. Si encuentras una URL así, la reparación es set-url limpio + reject del token viejo + rotar el token en el proveedor. GitHub además ya no acepta contraseñas en HTTPS desde 2021: el password siempre es un token o una llave SSH, nunca la contraseña de la cuenta.
Segundo, imponer credential.helper global. El agente corre git config --global credential.helper store para "arreglar" su 401 y de paso cambia la auth de todos los proyectos del humano, posiblemente a un helper inseguro. Toda configuración de helper que haga un agente va con --local y con justificación en el mensaje del commit o en el reporte del paso.
Tercero, loguear el secreto en el camino. git credential fill imprime password=... en stdout; si el agente captura esa salida en un log, un artifact de CI o el propio transcript del chat, el helper ya no importa: el secreto está en texto plano en otro lado. Redirige, usa y descarta; verifica con exit codes y con reject/approve, no inspeccionando el valor.

Checklist del agente antes de tocar auth
git config --show-origin --get-all credential.helper: saber qué helper responde antes de invocarfill.- URL del remote limpia (
git remote -vsinusuario:secreto@). - Helper configurado solo
--local; cero--global, cero--system. fillresuelve sin interacción; si pide terminal, detenerse y reportar.approvesolo tras push exitoso;rejectante el primer 401, luego rotar.- Cero
password=en logs, artifacts, transcripts o variables persistentes. - Cero
storeen disco compartido o contenedor con snapshot.
FAQ
¿fill puede pedir usuario y contraseña por terminal aunque haya helper?
Sí, cuando ningún helper tiene la credencial. Ese es el caso normal la primera vez en una máquina nueva. Para un agente sin TTY eso es un bloqueo reportable, no una invitación a pedir el token por chat.
¿Por qué reject antes de rotar y no después?
Porque el helper seguiría entregando el token muerto en cada fill y cada push fallaría igual. reject limpia el caché y el almacén; rotar en el proveedor sin reject deja al agente reintentando con el secreto viejo.
¿Vale credential.helper en el repo (.git/config) commiteado?
El helper vive en config local, que no se commitea. Lo que sí se versiona a veces es .gitconfig compartido o include.path; un agente nunca debe agregar un helper con store a un archivo versionado.
¿Y si el push usa SSH en vez de HTTPS?
Entonces git credential no interviene: la auth la resuelve el agente SSH y sus llaves. Esta guía aplica al flujo HTTPS con tokens, que es el que rompen la mayoría de los agentes en CI y contenedores.
¿Cada fill hace red?
No. fill solo consulta config y helpers locales; la red ocurre en el push/fetch posterior. Por eso fill es seguro de invocar para diagnosticar: es lectura local, cero efectos remotos.
Lecturas relacionadas
Sigue explorando Coding Agents y otras piezas para builders.

git hooks para coding agents: listar, no instalar ni --no-verify

git maintenance para coding agents: housekeeping programado, no gc a pelo

git verify-tag para coding agents: ningún release sin firma válida
