markdown · 3623 bytes Raw Blame History

GPG keys & commit signing

Uploading an OpenPGP public key lets shithub mark your signed commits and tags as Verified. Verification runs server-side against the bytes git stored in each commit object — there is no client-side trust ceremony.

1. Generate an OpenPGP key

Skip this if you already have a signing key (gpg --list-secret-keys --keyid-format=long shows what you have).

gpg --full-generate-key

Pick ECC (sign and encrypt) and Curve 25519 when prompted — ed25519 is the modern default and matches what gh recommends. The key must carry at least one user ID with an email address that you have verified on your shithub account, or the resulting signatures will fall back to the unverified_email reason.

Encryption-only keys (e.g. a key generated for --encrypt with no signing subkey) are accepted by shithub, but they can't verify commits. The REST response surfaces can_sign: false honestly when this is the case.

2. Export the public key

gpg --armor --export <KEY-ID-OR-EMAIL>

Copy the entire ASCII-armored block, including the -----BEGIN PGP PUBLIC KEY BLOCK----- and END lines.

3. Add the key in shithub

Settings → SSH and GPG keys → "New GPG key". Paste the block, give it a label (e.g., "laptop"), save.

The page shows the primary fingerprint shithub parsed; verify it matches gpg --fingerprint <KEY-ID> locally before relying on the badge.

4. Tell git to sign

git config --global user.signingkey <KEY-ID>
git config --global commit.gpgsign true
git config --global tag.gpgsign true

Use the email on your shithub account as user.email — the verification cross-check compares the signature's UID emails against your account's verified emails.

5. Push and see the badge

After your next signed push, the commit list, the single-commit page, and the tag list all show a green Verified pill. Click it for signer + verified-at details.

What the badge states mean

Pill Reason Meaning
Green "Verified" valid Signature parsed, cryptographically checked against a registered key, signing email matches a verified email on the key.
Yellow "Unverified" unknown_key Signature parsed but no uploaded key matches the signing subkey's fingerprint.
Yellow "Unverified" unverified_email Signature is valid for an uploaded key, but the signing email isn't verified on that key's account.
Yellow "Unverified" bad_email Signature is valid for an uploaded key, but the signing email isn't on the key at all.
Yellow "Unverified" expired_key Signature is valid, but the key was expired at signing time.
Yellow "Unverified" not_signing_key The key referenced isn't a signing key (capability bits missing).
Yellow "Unverified" malformed_signature The signature block didn't parse.
Yellow "Unverified" invalid Signature parsed but the cryptographic check failed.
no badge unsigned Git stored no signature header. This is the default; we don't render anything.

Retroactive verification

Uploading a key kicks off a background job that re-scans your existing commits across every repo and stamps the verification cache for the matches. Refresh the commit list a moment after upload — the badges appear without you doing anything.

Removing a key

Settings → SSH and GPG keys → "Delete" next to the GPG key row. The verification cache rows that resolved against the deleted key are invalidated; affected commits revert to no badge until another matching key is uploaded.

View source
1 # GPG keys & commit signing
2
3 Uploading an OpenPGP public key lets shithub mark your signed
4 commits and tags as **Verified**. Verification runs server-side
5 against the bytes git stored in each commit object — there is no
6 client-side trust ceremony.
7
8 ## 1. Generate an OpenPGP key
9
10 Skip this if you already have a signing key (`gpg --list-secret-keys --keyid-format=long`
11 shows what you have).
12
13 ```sh
14 gpg --full-generate-key
15 ```
16
17 Pick `ECC (sign and encrypt)` and `Curve 25519` when prompted —
18 ed25519 is the modern default and matches what `gh` recommends.
19 The key must carry **at least one user ID** with an email address
20 that you have verified on your shithub account, or the resulting
21 signatures will fall back to the `unverified_email` reason.
22
23 > Encryption-only keys (e.g. a key generated for `--encrypt`
24 > with no signing subkey) are accepted by shithub, but they can't
25 > verify commits. The REST response surfaces `can_sign: false`
26 > honestly when this is the case.
27
28 ## 2. Export the public key
29
30 ```sh
31 gpg --armor --export <KEY-ID-OR-EMAIL>
32 ```
33
34 Copy the entire ASCII-armored block, including the
35 `-----BEGIN PGP PUBLIC KEY BLOCK-----` and `END` lines.
36
37 ## 3. Add the key in shithub
38
39 Settings → **SSH and GPG keys** → "New GPG key". Paste the block,
40 give it a label (e.g., "laptop"), save.
41
42 The page shows the primary fingerprint shithub parsed; verify it
43 matches `gpg --fingerprint <KEY-ID>` locally before relying on
44 the badge.
45
46 ## 4. Tell git to sign
47
48 ```sh
49 git config --global user.signingkey <KEY-ID>
50 git config --global commit.gpgsign true
51 git config --global tag.gpgsign true
52 ```
53
54 Use the email on your shithub account as `user.email` — the
55 verification cross-check compares the signature's UID emails
56 against your account's verified emails.
57
58 ## 5. Push and see the badge
59
60 After your next signed push, the commit list, the single-commit
61 page, and the tag list all show a green **Verified** pill. Click
62 it for signer + verified-at details.
63
64 ## What the badge states mean
65
66 | Pill | Reason | Meaning |
67 |------|--------|---------|
68 | Green "Verified" | `valid` | Signature parsed, cryptographically checked against a registered key, signing email matches a verified email on the key. |
69 | Yellow "Unverified" | `unknown_key` | Signature parsed but no uploaded key matches the signing subkey's fingerprint. |
70 | Yellow "Unverified" | `unverified_email` | Signature is valid for an uploaded key, but the signing email isn't verified on that key's account. |
71 | Yellow "Unverified" | `bad_email` | Signature is valid for an uploaded key, but the signing email isn't on the key at all. |
72 | Yellow "Unverified" | `expired_key` | Signature is valid, but the key was expired at signing time. |
73 | Yellow "Unverified" | `not_signing_key` | The key referenced isn't a signing key (capability bits missing). |
74 | Yellow "Unverified" | `malformed_signature` | The signature block didn't parse. |
75 | Yellow "Unverified" | `invalid` | Signature parsed but the cryptographic check failed. |
76 | _no badge_ | `unsigned` | Git stored no signature header. This is the default; we don't render anything. |
77
78 ## Retroactive verification
79
80 Uploading a key kicks off a background job that re-scans your
81 existing commits across every repo and stamps the verification
82 cache for the matches. Refresh the commit list a moment after
83 upload — the badges appear without you doing anything.
84
85 ## Removing a key
86
87 Settings → **SSH and GPG keys** → "Delete" next to the GPG key
88 row. The verification cache rows that resolved against the
89 deleted key are invalidated; affected commits revert to no
90 badge until another matching key is uploaded.