Guía9 min

git verify-commit para coding agents: firma válida no es firma confiable

Resumen

git verify-commit valida la firma GPG o SSH de un commit sin mover HEAD ni tocar el working tree. Un SHA por invocación, exit code como contrato y verify-tag para tags anotados. Cero re-firmar con amend, cero --raw volcado al contexto del agente y cero asumir confianza por una firma válida. Git 2.50.1.

GitHub
Verificación de firma de commit: verify-commit valida el objeto firmado sin mover HEAD ni tocar el working tree

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 verify-commit valida la firma de un commit sin mover HEAD, sin tocar el índice y sin tocar el working tree. El man (git-verify-commit(1); git-scm.com/docs/git-verify-commit; man local Git 2.50.1.428.g0e8243, 2025-07-22; binario Git 2.50.1 / Apple Git-155): Validates the GPG signature created by git commit -S on the commit objects given on the command line. SYNOPSIS: git verify-commit [-v | --verbose] [--raw] <commit>....

Es la herramienta de procedencia del plumbing: antes de construir sobre un SHA que otro agente (o humano) dejó en el worktree, verificas que ese SHA lo firmó quien dice haberlo firmado. Lectura pura, cero efectos.

No es show. show imprime el objeto para inspeccionarlo; verify-commit lo valida y responde con exit code. Son complementos: show para ver, verify-commit para confiar. En ambos rige la misma higiene: cero volcar la salida cruda al contexto del LLM.

Tampoco es fsck. fsck recorre alcanzabilidad e integridad del object database; verify-commit no valida hashes ni objetos dangling, solo la firma del commit que le pasas. Ni es gc: nada aquí empaqueta, recorta ni escribe.

Contrato: git verify-commit <sha>. Exit code 0 = firma válida. Cero --raw al prompt. Cero re-firmar. Cero confundir "firma válida" con "firmante confiable". Tags anotados = git verify-tag.

Qué hace (y qué no)

git verify-commit toma uno o más SHAs y comprueba la firma GPG (o SSH, desde Git 2.34 con gpg.format ssh) que git commit -S dejó en el objeto commit. Si el commit no está firmado, falla. Si la firma no corresponde al contenido —porque alguien reescribió el commit—, falla. Si todo cuadra, exit 0 y una línea humana: Good signature.

Lo que no hace, y esto es lo que más agentes malinterpretan:

  • No camina el historial. Verificar HEAD no verifica a sus padres. Un merge firmado puede esconder padres sin firmar. Si la política es "todo firmado", verifica cada SHA del rango, no solo la punta.
  • No decide confianza. Una firma válida de una clave desconocida sigue siendo válida para verify-commit. Validez criptográfica ≠ firmante autorizado. La confianza vive en otro lado: gpg.ssh.allowedSignersFile para SSH, tu web of trust o tu política de claves para GPG.
  • No firma nada. Es solo lectura. No hay flag que "arregle" un commit sin firmar; el remedio es un commit nuevo (firmado), nunca reescribir el existente.

Diagrama del flujo firmar-verificar: commit -S crea el objeto firmado y verify-commit lo valida sin mover HEAD

Las dos flags (solo hay dos)

FlagQué haceCuándo usarla
-v, --verboseImprime el contenido del objeto commit antes de validarloDepuración humana; nunca en el loop del agente
--rawVuelca el estado GPG crudo a stderr en vez del resumen legibleIntegración con scripts que parsean; nunca al contexto del LLM

Sin flags es el modo correcto para agentes: una línea legible + exit code. El exit code es el contrato; el texto es para humanos.

verify-tag: el hermano para releases

Los tags anotados firmados (git tag -s) se verifican con git verify-tag [-v | --verbose] <tag>... (git-scm.com/docs/git-verify-tag). Misma filosofía: lectura pura, exit code como respuesta. Regla operativa: el agente verifica el tag del release antes de desplegarlo o de hacer checkout sobre él. Un tag liviano (git tag <nombre> sin -a/-s) no tiene objeto propio ni firma: verify-tag sobre él no prueba nada. Si tu pipeline exige procedencia, exige tags anotados firmados.

Receta para coding agents

# 1. Verificar la punta antes de construir sobre ella
git verify-commit HEAD && echo FIRMA-OK

# 2. Verificar un rango completo (cada SHA, no solo la punta)
for sha in $(git rev-list HEAD~5..HEAD); do
  git verify-commit "$sha" || echo "SIN-FIRMA-O-MALA: $sha"
done

# 3. Verificar el tag del release antes del deploy
git verify-tag v1.4.0 && echo TAG-OK

Tres reglas duras:

  1. Cero re-firmar. Si un commit no está firmado, no lo "arreglas" con commit --amend -S: eso crea un SHA nuevo y rompe la historia compartida (force-push: nunca --force para empujar el rewrite). El commit sin firmar se deja como está o se reemplaza con un commit nuevo por el canal normal (rama + PR).
  2. Cero --raw y cero -v al contexto. La salida cruda de GPG y el volcado del objeto son ruido caro y, peor, pueden arrastrar key IDs y metadata al prompt. El agente consume el exit code; el humano lee el texto.
  3. La signing key no vive en el prompt. Dónde guardar user.signingKey, el allowedSignersFile o la passphrase es tema de secretos, no de este comando. verify-commit solo verifica; nunca le pases material de firma.

Estados de verificación: firma válida, commit sin firmar y firma rota, cada uno con su acción

Checklist

  • git verify-commit HEAD en exit 0 antes de basar trabajo en un SHA ajeno
  • Rango completo verificado SHA por SHA cuando la política es "todo firmado"
  • git verify-tag sobre el tag anotado antes de deploy o checkout de release
  • Firmante cruzado contra lista de claves autorizadas, no solo "firma válida"
  • Cero --raw, cero -v en el loop del agente; exit code como contrato
  • Cero amend re-firmado; commit nuevo por rama + PR si falta una firma

FAQ

¿Verify-commit comprueba que el autor es quien dice ser? No. Comprueba que el contenido lo firmó el dueño de la clave. Si la clave no está en tu lista de autorizadas, la firma válida no prueba autorización.

¿Y si el commit no tiene firma? Falla con non-zero. Eso es información, no error del comando: el commit existe y es legible, simplemente no hay procedencia criptográfica. Decide tu política (bloquear, advertir, ignorar) fuera del comando.

¿Verifica merges recursivamente? No. Verifica la firma del objeto merge, no la de sus padres. Para cobertura total, itera git rev-list como en la receta.

¿SSH o GPG? Ambos funcionan. SSH (gpg.format ssh + allowedSignersFile) es más simple de operar para equipos que ya gestionan claves SSH; GPG tiene ecosistema de web of trust. verify-commit no distingue: valida lo que encuentra.

¿Dónde encaja en el flujo del agente? En la puerta de entrada: al tomar un worktree, al recibir un SHA de otro agente y al preparar un deploy. Es un gate barato —milisegundos, cero escritura— que va antes del trabajo caro. Si quieres el setup completo del agente, empieza por el curso de instalar agente.