Supply Chain

Git Commit Signing: Proving Who Wrote the Code

July 23, 2026 10 min read Haven Team

The author name and email on a git commit are just text fields. Git will happily record a commit as coming from anyone, because nothing checks that the person setting those fields is the person they claim to be. Commit signing is what turns that unenforced label into a cryptographic claim you can actually verify. For projects that ship code people depend on, it is the difference between trusting a name and trusting a proof.


Here is a fact that surprises many developers the first time they see it. Anyone can run two commands, git config user.name "Linus Torvalds" and git config user.email torvalds@linux-foundation.org, and every commit they make afterward will show up in the log attributed to that identity. No password, no check, no barrier. The author and committer fields in a git object are set by the person making the commit, and git stores exactly what they typed.

For most day-to-day work this does not matter. But for open-source projects, for any code that flows into other people's builds, it is a real problem. If an attacker can push commits that appear to come from a trusted maintainer, they can slip changes into a codebase under a borrowed reputation. This is one thread of the larger software supply chain problem, and commit signing is the piece that addresses authorship specifically.

What a signature actually covers

When you sign a commit, git computes a cryptographic signature over the commit object, which includes the tree hash, the parent commit, the author and committer fields, and the commit message. That signature is stored in the commit itself. Because a digital signature can only be produced by someone holding the corresponding private key, a valid signature proves two things at once: that the named signer made the commit, and that not a single byte of it has changed since.

That second property matters more than it first appears. Git history is a chain: each commit references its parent by hash. A signature on a commit therefore implicitly commits to its entire ancestry, because changing any earlier commit would change the hashes all the way up and invalidate the signature. Signing the tip of a branch is a statement about everything beneath it.

Signing is not encryption

A signed commit is not a secret commit. Signing adds a proof of authorship and integrity; it does nothing to hide the contents. The code in a signed commit is exactly as readable as in an unsigned one. If you need the repository contents themselves kept confidential, that is a separate concern from signing, handled by encrypting the repository or restricting access to it.

The three ways to sign

Git supports three signing backends today, and they represent three different eras of thinking about the problem.

OpenPGP, the original

For most of git's life, signing meant PGP. You generate an OpenPGP key, tell git about it with git config user.signingkey, and sign commits with git commit -S or tags with git tag -s. Verifiers need your public key to check the signature, which they get through the usual PGP channels, increasingly through Web Key Directory. PGP signing is powerful and portable, but it carries all the operational weight of PGP: key management, expiry, revocation, and the general friction that has kept PGP out of most developers' hands.

SSH signing, the pragmatic turn

Since version 2.34, released in late 2021, git can sign with SSH keys. You set gpg.format ssh and point user.signingkey at an SSH public key, most likely the same key you already use to push to your remote. This was a significant usability move, because nearly every developer already has an SSH key and already understands it. Verification uses an allowed_signers file that maps identities to their permitted public keys, so a project can define exactly whose signatures it accepts. SSH signing removed the single biggest barrier, which was that PGP was a whole separate thing to learn.

Sigstore and keyless signing

The newest approach, embodied by gitsign from the Sigstore project, tries to remove long-lived keys entirely. Instead of holding a signing key, you authenticate through an existing identity provider using OIDC, and Sigstore issues a short-lived certificate bound to that identity, valid for only a few minutes. The signature and certificate are recorded in Rekor, a public transparency log, so there is a permanent, auditable record that a given identity signed a given commit at a given time. The appeal is that there is no private key to protect, lose, or leak. The trade is that you depend on the identity provider and the Sigstore infrastructure.

Method Key to manage Best fit
OpenPGP Long-lived PGP key Projects already in the PGP ecosystem
SSH Existing SSH key Most individual developers today
Sigstore (gitsign) None, keyless via OIDC CI pipelines, org-scale automation

The gap between signed and verified

A signature is worthless to a verifier who does not know which key to trust. This is the part that platforms tend to gloss over. When a hosting service shows a green "Verified" badge next to a commit, it is telling you that the signature matched a key the platform has on file for that account. That is a useful check, but it is the platform's judgment, not yours, and it only covers keys that account uploaded.

A signature answers "was this made by the holder of key X." Whether you should trust key X in the first place is a separate question that no signature can answer for you. The boundary every signing scheme shares

For your own verification, you decide whose keys go in your allowed_signers file or your PGP keyring. That decision is where the real trust lives, and it is the same trust-establishment problem that shows up everywhere in cryptography, from trust-on-first-use to formal identity systems. Signing does not remove the need to establish trust in a key. It makes that trust, once established, mechanically enforceable on every commit thereafter.

Turning it on

Enabling signing is a few lines of config. The most common modern setup, using an SSH key, looks like this:

From then on, your commits carry signatures without extra effort. If you use Ed25519 SSH keys, which are the sensible default, you get compact, fast signatures with a well-regarded curve underneath. Enforcing that received commits are signed, in a CI pipeline or a branch protection rule, is the other half of the story, and it is what converts signing from a personal habit into a project-wide guarantee.

Why this belongs in an honest security posture

Provenance is a quiet part of security that gets attention only after something goes wrong. Knowing that the code you run came from who it claims to, unaltered, is foundational to being able to trust anything built on top of it. For a project whose entire value rests on users trusting the code, signed commits paired with reproducible builds let outside parties check the chain from a specific author all the way to the artifact they installed.

That verifiable chain is exactly why we sign our commits and publish our build process rather than asking anyone to take our word for it. A security product that says "trust us" has already lost the argument. Commit signing is one of the plain, checkable mechanisms that lets the claim rest on evidence instead. It is not glamorous, and it will never be the headline feature of anything. It is just part of what it means to ship code you expect people to stake their safety on.

Try Haven free for 15 days

Encrypted email and chat in one app. No credit card required.

Get Started →