# How to verify your commits on GitHub

Git doesn't check who you are. The author name and email on a commit are whatever is in your config, so anyone can make a commit that claims to be from you:

```bash
git -c user.name="Your Name" -c user.email="you@example.com" commit -m "totally legit"
```

Push that to GitHub and it will show up with your avatar next to it. Nothing about the commit proves you wrote it.

Signing fixes that. You sign each commit with a key only you hold, GitHub checks the signature against the public key on your account, and the commit gets a green **Verified** badge. It takes about ten minutes to set up, and after that you don't have to think about it.

## Why bother

For a personal project it can feel like ceremony. A few reasons it isn't:

- **Supply chain.** A lot of attacks on open source come down to "someone pushed code that looked like it came from a trusted maintainer." Signed commits, together with branch protection that *requires* them, make that harder.
- **Some repositories require it.** Many organizations turn on "Require signed commits" for protected branches. If your commits aren't signed, you can't merge.
- **Vigilant mode.** Once you sign everything, you can turn on [vigilant mode](https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits). GitHub then marks any commit that claims to be you but isn't signed as **Unverified**, so impersonation stands out instead of blending in.

## 1\. Create a GPG key

On macOS, install GnuPG and a pinentry program first (pinentry is the dialog that asks for your passphrase):

```bash
brew install gnupg pinentry-mac
```

Then generate the key:

```bash
gpg --full-generate-key
```

The defaults in recent GnuPG versions (ECC, ed25519) are fine. Two things matter:

- **The email has to be one you've verified on GitHub.** If you use the `noreply` address ([`12345+user@users.noreply.github.com`](mailto:12345+user@users.noreply.github.com)), put that one in the key.
- **Use a passphrase.** The key lives on your disk. Without a passphrase, anyone who gets a copy of `~/.gnupg` can sign as you.

## 2\. Find your key ID

```bash
gpg --list-secret-keys --keyid-format=long
```

```text
sec   ed25519/3AA5C34371567BD2 2026-11-04 [SC]
      ...
uid                 [ultimate] Your Name <you@example.com>
```

The key ID is the part after the slash: `3AA5C34371567BD2`.

## 3\. Add the public key to GitHub

Export the public key:

```bash
gpg --armor --export 3AA5C34371567BD2
```

Copy the whole output, including the `-----BEGIN PGP PUBLIC KEY BLOCK-----` and `-----END PGP PUBLIC KEY BLOCK-----` lines. Paste it in **GitHub → Settings → SSH and GPG keys → New GPG key**.

## 4\. Tell Git to sign everything

```bash
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

With `commit.gpgsign` on you don't need `-S` on every commit; Git signs them all.

GPG also needs to know which terminal to ask for the passphrase on. Add this to your `~/.zshrc` (or `~/.bashrc`):

```bash
export GPG_TTY=$(tty)
```

Without it, you'll probably see this the first time you commit:

```text
error: gpg failed to sign the data
fatal: failed to write commit object
```

## 5\. Stop typing your passphrase on every commit

`gpg-agent` caches your passphrase after you type it once. The default timeout is short (10 minutes), so you can raise it in `~/.gnupg/gpg-agent.conf`:

```text
default-cache-ttl 3600
max-cache-ttl 86400
pinentry-program /opt/homebrew/bin/pinentry-mac
```

- `default-cache-ttl` is how many seconds the passphrase stays cached after you last used it (here, one hour).
- `max-cache-ttl` is the hard limit, however often you use it (here, one day).
- `pinentry-program` makes macOS show a native dialog instead of asking in the terminal. On Intel Macs, the path is `/usr/local/bin/pinentry-mac`.

Restart the agent so it reads the new config:

```bash
gpgconf --kill gpg-agent
```

## 6\. Check that it works

Make a commit and look at the signature:

```bash
git commit --allow-empty -m "test signing"
git log --show-signature -1
```

```text
gpg: Signature made Wed Nov  4 11:40:12 2026 CET
gpg:                using EDDSA key 3AA5C34371567BD2
gpg: Good signature from "Your Name <you@example.com>" [ultimate]
```

Push it, and the commit on GitHub shows the **Verified** badge.

## The shortcut: sign with your SSH key

Since Git 2.34, you can sign with the SSH key you already use to push, with no GPG involved:

```bash
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
```

Then add the same public key on GitHub again, but this time choose **Signing Key** as the key type (an Authentication key can't verify signatures).

It's less setup, and if your SSH key is already in a hardware key or a password manager like 1Password, you get that for free. GPG is still the more common choice for signing tags and release artifacts, and it's what most guides assume, which is why I covered it first.

## Wrapping up

Ten minutes of setup, and every commit you push from now on can be traced back to a key only you hold. If you maintain anything other people depend on, turn on vigilant mode too, and think about requiring signed commits on your main branch. It's one of the cheapest supply chain controls there is.
