bash + batch no dependencies MIT licensed

Sign every commit.
No more "Unverified" badges.

One script sets up SSH commit signing end to end — detects the GitHub CLI, offers to migrate you off GPG, generates a key dedicated only to signing, uploads it to GitHub, and proves it works with a real signed test commit before it's done.

curl -fsSL https://raw.githubusercontent.com/hbenali/commit-signing-web/main/scripts/git-ssh-signing-setup.sh -o git-ssh-signing-setup.sh && chmod +x git-ssh-signing-setup.sh && ./git-ssh-signing-setup.sh
curl not installed? Here's the wget command
wget -q https://raw.githubusercontent.com/hbenali/commit-signing-web/main/scripts/git-ssh-signing-setup.sh -O git-ssh-signing-setup.sh && chmod +x git-ssh-signing-setup.sh && ./git-ssh-signing-setup.sh
curl -fsSL https://raw.githubusercontent.com/hbenali/commit-signing-web/main/scripts/git-ssh-signing-setup.bat -o git-ssh-signing-setup.bat && git-ssh-signing-setup.bat

Downloads first, then runs — the script is interactive and asks before changing anything, so we never pipe it straight into a shell. Read the source before you run it.

Before you run it

Four tools, and you likely already have most of them.

curl or wget

To fetch the script itself. Most distros ship one, but minimal images (some containers, server installs) may have neither — install with your distro's package manager, e.g. sudo apt install curl / sudo dnf install curl.

Git 2.34+

Required for SSH commit signing support. git-scm.com/downloads

OpenSSH (ssh-keygen)

Built into macOS, Linux, and Windows 10+. If missing on Windows: enable the OpenSSH Client feature — Git for Windows also bundles it.

GitHub CLI (gh)

Optional but recommended — automates the upload step. cli.github.com. Without it, the script prints manual copy-paste steps instead.

What it sets up

Six steps, each with checks and confirmations along the way — nothing happens without you seeing it first.

1

Pre-flight checks

Verifies git ≥ 2.34, ssh-keygen, and the GitHub CLI before touching anything.

2

GPG migration

Detects an existing GPG signing setup and offers to back it up and switch to SSH, or leave it alone.

3

Dedicated signing key

Generates — or lets you pick — a key used only for signing, never for push/pull authentication.

4

Upload via gh

Uploads the key as a Signing Key, auto-recovers from a missing admin:ssh_signing_key scope, and warns before reusing it for Git auth.

5

Commit & tag signing

Sets gpg.format=ssh and your signing key, then asks separately about commit vs. tag signing.

6

Real verification

Makes an actual signed test commit and checks git log --show-signature reports "Good" before declaring success.

Prefer to do it by hand?

Same result, no script — pick your signing method.

  1. Generate a dedicated signing key

    Keep it separate from any key you use for SSH authentication.

    ssh-keygen -t ed25519 -C "you@example.com" -f ~/.ssh/id_ed25519_signing
    ssh-keygen -t ed25519 -C "you@example.com" -f %USERPROFILE%\.ssh\id_ed25519_signing
  2. Copy the public key
    cat ~/.ssh/id_ed25519_signing.pub
    type %USERPROFILE%\.ssh\id_ed25519_signing.pub
  3. Add it on GitHub — as a Signing Key

    Go to github.com/settings/ssh/new, paste the key, and set the key type to Signing Key (not Authentication Key).

  4. Configure git
    git config --global gpg.format ssh git config --global user.signingkey ~/.ssh/id_ed25519_signing.pub git config --global commit.gpgsign true
    git config --global gpg.format ssh git config --global user.signingkey %USERPROFILE%\.ssh\id_ed25519_signing.pub git config --global commit.gpgsign true
  5. Optional: verify signatures locally

    Lets git log --show-signature confirm "Good" instead of just "no signature error".

    echo "$(git config --global user.email) namespaces=\"git\" $(cat ~/.ssh/id_ed25519_signing.pub)" >> ~/.ssh/allowed_signers git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
    for /f "delims=" %i in ('git config --global user.email') do set EMAIL=%i for /f "usebackq tokens=1,2" %a in ("%USERPROFILE%\.ssh\id_ed25519_signing.pub") do echo %EMAIL% namespaces="git" %a %b>>%USERPROFILE%\.ssh\allowed_signers git config --global gpg.ssh.allowedSignersFile %USERPROFILE%\.ssh\allowed_signers
  6. Test it
    git commit -S -m "test" --allow-empty && git log --show-signature -1
  1. Generate a key

    Ed25519, signing-only, expiring in 2 years — adjust as you like.

    gpg --quick-generate-key "Your Name <you@example.com>" ed25519 sign 2y
  2. Find the key ID
    gpg --list-secret-keys --keyid-format=long
  3. Export the public key
    gpg --armor --export <KEY_ID>
  4. Add it on GitHub

    Go to github.com/settings/gpg/new and paste the exported block.

  5. Configure git
    git config --global user.signingkey <KEY_ID> git config --global commit.gpgsign true
  6. Test it
    git commit -S -m "test" --allow-empty && git log --show-signature -1

GPG still works fine

It's just more machinery — a separate keyring and passphrase-agent setup — for a result SSH signing gets from a key format you probably already have. If you've already got a GPG setup, the script detects it and offers to migrate you to SSH instead of fighting it.

The commands above are identical on Windows — install Gpg4win first, then run them as-is in cmd or PowerShell.

Check a GitHub account

Look up any public GitHub username's registered signing keys — SSH and GPG — straight from GitHub's public API. Nothing is sent anywhere but github.com; this runs entirely in your browser.

A few things worth knowing

Don't reuse your signing key for push authentication

  • A key used for both loses independence — rotating or revoking one breaks the other.
  • When gh auth login asks you to select an SSH key for Git operations, pick a different one (or use HTTPS/token auth, which skips the question entirely).
  • If gh has more than one account logged in, double-check which one you're uploading the signing key to.

Signing key ≠ authentication key on GitHub

GitHub tracks these as two separate key types under Settings → SSH and GPG keys. Adding a key as an Authentication key does not make it usable for signing, and vice versa — that's by design.

FAQ

New to Git entirely?

This site assumes you already use Git day to day. If you don't yet: install it, then set your identity once with git config --global user.name "Your Name" and git config --global user.email "you@example.com". The everyday loop is git add (stage changes), git commit -m "message" (save them), and git push (send them to a remote like GitHub). The official Git documentation — especially the free Pro Git book — is the best place to actually learn it properly; come back here once commit and push feel routine.

Why do my old commits still show "Unverified"?

Signing only applies going forward. Past commits keep whatever signature (or lack of one) they were made with — re-signing history means rewriting it, which isn't something this script does automatically.

What if gh isn't installed or isn't logged in?

The script falls back to printing the exact manual steps — copy the public key, and add it at github.com/settings/ssh/new yourself. Nothing blocks on the GitHub CLI being available.

What if my token is missing the signing-key scope?

GitHub added a dedicated admin:ssh_signing_key scope for managing signing keys. If your upload fails because of it, the script recognizes the exact error and offers to run gh auth refresh -h github.com -s admin:ssh_signing_key for you, then retries automatically.

Does this work on Windows?

Yes — a .bat version ships alongside the .sh one, with the same checks, GPG-migration prompt, and verification flow adapted for cmd.exe.

Is it safe to just curl and run this?

The script downloads to a file first and only then executes — it never pipes into a shell — specifically so you have the chance to open and read it before running. Every change it makes is behind its own confirmation prompt.

Does this work with GitLab or Gitea instead of GitHub?

Commit signing itself — SSH or GPG — is a git feature, not a GitHub one: it happens entirely on your machine and doesn't care which remote you push to. Only the "upload your key" step differs. The script's gh integration is GitHub-specific, so on GitLab or Gitea it'll fall back to manual instructions — add the same public key yourself under GitLab's SSH Keys settings (set usage type to "Signing") or GPG Keys, or your Gitea instance's Settings → SSH/GPG Keys page. Everything else — the dedicated key, git config, and verification steps — is identical.