Deploy: GPG keys + commit signature verification (S51)
S51 ships the GPG-keys storage + the verification orchestrator
- the Verified-badge rendering + a bulk-backfill admin command, all in one feature-branch merge to trunk. The migrations are additive and the rendering paths fall back to "unsigned" (no badge) on cache miss, so the rollout is safe to do in-band — no maintenance window required.
Pre-deploy
-
Note the currently-deployed
ldd /opt/shithub/bin/shithubdoutput. shithub uses pure-Go OpenPGP viagithub.com/ProtonMail/go-crypto; no new dynamic deps are expected. Diffing post-deploy confirms.ssh root@shithub.sh "ldd /opt/shithub/bin/shithubd" > /tmp/ldd-pre.txt -
Capture migration status as a baseline:
ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate status" \ > /tmp/migrate-pre.txt
Deploy
-
Apply migrations. Three new versions land:
0068_user_gpg_keys.sql— primary keys table0069_user_gpg_subkeys.sql— subkey fingerprint reverse-lookup0070_commit_verification_cache.sql— verification result cache
ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate up" ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate status"migrate statusshould report all three at version applied. -
Quick sanity-check the three new tables exist and are empty:
ssh root@shithub.sh "sudo -u shithub psql -d shithub -c ' SELECT count(*) FROM user_gpg_keys; SELECT count(*) FROM user_gpg_subkeys; SELECT count(*) FROM commit_verification_cache;'"Expect
0 / 0 / 0. -
Rolling-restart shithubd. The deploy unit drops one replica at a time; the health endpoint reports 200 once a replica has finished its startup migration check.
ssh root@shithub.sh "sudo systemctl restart shithubd@*" -
Confirm the
gpg-keyscapability landed:curl -fsS https://shithub.sh/api/v1/meta | jq '.capabilities | index("gpg-keys")'Non-null output confirms.
Bulk backfill
-
Once-off after deploy. Walk every repo's default branch and stamp the verification cache for commits whose signing subkey matches an already-uploaded GPG key. This is what lights up existing signed commits for users whose keys we already had (mostly admins / early testers; the long tail gets covered on next key upload).
ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd gpg-backfill-all"The command is idempotent and resumable — re-running picks up where it left off (cache rows are upserted; existing rows short-circuit the walk for that commit). Progress is logged per repo. On a fresh deploy with no pre-existing GPG keys the command is a no-op and exits in a few seconds.
To limit to a single repo (e.g. for spot-checking after the bulk run):
ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd \ gpg-backfill-all --repo=<owner>/<name>"
Smoke-test
-
Log in as a test account and exercise the user-visible flow:
- Navigate to
/settings/keys. Confirm the page renders with the combined SSH + GPG sections. - Upload a test GPG key whose UID email matches a verified email on the test account.
- Navigate to a repo that has signed commits authored by that account on its default branch. The commit list should show green Verified pills within a few seconds of the upload (the backfill worker picks up the eager dispatch).
- Open
/{owner}/{repo}/commits/{sha}for one of those commits. The header should show the larger Verified badge with a click-popover.
- Navigate to
-
Hit the REST surface with a
repo:readPAT:curl -fsS -H "Authorization: Bearer $PAT" \ https://shithub.sh/api/v1/repos/<owner>/<name>/commits | \ jq '.[0].verification'Expect
{verified, reason, signature, payload, verified_at}on every entry.
Rollback
The migrations are additive; rollback is migrate down 3 to
reverse them. Re-running with a server build that includes
0066–0068 will re-apply on the next deploy with no data loss
(the tables get rebuilt empty; the next eager dispatch repopulates).
If a bad server build forces a rollback to a pre-S51 binary:
ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate down 3"
This drops the three new tables. Any verification cache rows are lost; the bulk backfill on the subsequent re-deploy regenerates them.
See also
- User docs: GPG keys & commit signing
- API reference: GPG keys, Signature verification on commits
View source
| 1 | # Deploy: GPG keys + commit signature verification (S51) |
| 2 | |
| 3 | S51 ships the GPG-keys storage + the verification orchestrator |
| 4 | + the Verified-badge rendering + a bulk-backfill admin command, |
| 5 | all in one feature-branch merge to trunk. The migrations are |
| 6 | additive and the rendering paths fall back to "unsigned" |
| 7 | (no badge) on cache miss, so the rollout is safe to do |
| 8 | in-band — no maintenance window required. |
| 9 | |
| 10 | ## Pre-deploy |
| 11 | |
| 12 | 1. Note the currently-deployed `ldd /opt/shithub/bin/shithubd` |
| 13 | output. shithub uses pure-Go OpenPGP via |
| 14 | `github.com/ProtonMail/go-crypto`; no new dynamic deps are |
| 15 | expected. Diffing post-deploy confirms. |
| 16 | |
| 17 | ```sh |
| 18 | ssh root@shithub.sh "ldd /opt/shithub/bin/shithubd" > /tmp/ldd-pre.txt |
| 19 | ``` |
| 20 | |
| 21 | 2. Capture migration status as a baseline: |
| 22 | |
| 23 | ```sh |
| 24 | ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate status" \ |
| 25 | > /tmp/migrate-pre.txt |
| 26 | ``` |
| 27 | |
| 28 | ## Deploy |
| 29 | |
| 30 | 3. Apply migrations. Three new versions land: |
| 31 | - `0068_user_gpg_keys.sql` — primary keys table |
| 32 | - `0069_user_gpg_subkeys.sql` — subkey fingerprint reverse-lookup |
| 33 | - `0070_commit_verification_cache.sql` — verification result cache |
| 34 | |
| 35 | ```sh |
| 36 | ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate up" |
| 37 | ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate status" |
| 38 | ``` |
| 39 | |
| 40 | `migrate status` should report all three at version applied. |
| 41 | |
| 42 | 4. Quick sanity-check the three new tables exist and are empty: |
| 43 | |
| 44 | ```sh |
| 45 | ssh root@shithub.sh "sudo -u shithub psql -d shithub -c ' |
| 46 | SELECT count(*) FROM user_gpg_keys; |
| 47 | SELECT count(*) FROM user_gpg_subkeys; |
| 48 | SELECT count(*) FROM commit_verification_cache;'" |
| 49 | ``` |
| 50 | |
| 51 | Expect `0 / 0 / 0`. |
| 52 | |
| 53 | 5. Rolling-restart shithubd. The deploy unit drops one replica |
| 54 | at a time; the health endpoint reports 200 once a replica |
| 55 | has finished its startup migration check. |
| 56 | |
| 57 | ```sh |
| 58 | ssh root@shithub.sh "sudo systemctl restart shithubd@*" |
| 59 | ``` |
| 60 | |
| 61 | 6. Confirm the `gpg-keys` capability landed: |
| 62 | |
| 63 | ```sh |
| 64 | curl -fsS https://shithub.sh/api/v1/meta | jq '.capabilities | index("gpg-keys")' |
| 65 | ``` |
| 66 | |
| 67 | Non-null output confirms. |
| 68 | |
| 69 | ## Bulk backfill |
| 70 | |
| 71 | 7. **Once-off after deploy.** Walk every repo's default branch |
| 72 | and stamp the verification cache for commits whose signing |
| 73 | subkey matches an already-uploaded GPG key. This is what |
| 74 | lights up existing signed commits for users whose keys we |
| 75 | already had (mostly admins / early testers; the long tail |
| 76 | gets covered on next key upload). |
| 77 | |
| 78 | ```sh |
| 79 | ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd gpg-backfill-all" |
| 80 | ``` |
| 81 | |
| 82 | The command is idempotent and resumable — re-running picks |
| 83 | up where it left off (cache rows are upserted; existing rows |
| 84 | short-circuit the walk for that commit). Progress is logged |
| 85 | per repo. On a fresh deploy with no pre-existing GPG keys |
| 86 | the command is a no-op and exits in a few seconds. |
| 87 | |
| 88 | To limit to a single repo (e.g. for spot-checking after the |
| 89 | bulk run): |
| 90 | |
| 91 | ```sh |
| 92 | ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd \ |
| 93 | gpg-backfill-all --repo=<owner>/<name>" |
| 94 | ``` |
| 95 | |
| 96 | ## Smoke-test |
| 97 | |
| 98 | 8. Log in as a test account and exercise the user-visible flow: |
| 99 | |
| 100 | - Navigate to `/settings/keys`. Confirm the page renders with |
| 101 | the combined SSH + GPG sections. |
| 102 | - Upload a test GPG key whose UID email matches a verified |
| 103 | email on the test account. |
| 104 | - Navigate to a repo that has signed commits authored by |
| 105 | that account on its default branch. The commit list should |
| 106 | show green **Verified** pills within a few seconds of the |
| 107 | upload (the backfill worker picks up the eager dispatch). |
| 108 | - Open `/{owner}/{repo}/commits/{sha}` for one of those |
| 109 | commits. The header should show the larger Verified badge |
| 110 | with a click-popover. |
| 111 | |
| 112 | 9. Hit the REST surface with a `repo:read` PAT: |
| 113 | |
| 114 | ```sh |
| 115 | curl -fsS -H "Authorization: Bearer $PAT" \ |
| 116 | https://shithub.sh/api/v1/repos/<owner>/<name>/commits | \ |
| 117 | jq '.[0].verification' |
| 118 | ``` |
| 119 | |
| 120 | Expect `{verified, reason, signature, payload, verified_at}` |
| 121 | on every entry. |
| 122 | |
| 123 | ## Rollback |
| 124 | |
| 125 | The migrations are additive; rollback is `migrate down 3` to |
| 126 | reverse them. Re-running with a server build that includes |
| 127 | 0066–0068 will re-apply on the next deploy with no data loss |
| 128 | (the tables get rebuilt empty; the next eager dispatch repopulates). |
| 129 | |
| 130 | If a bad server build forces a rollback to a pre-S51 binary: |
| 131 | |
| 132 | ```sh |
| 133 | ssh root@shithub.sh "sudo -u shithub /opt/shithub/bin/shithubd migrate down 3" |
| 134 | ``` |
| 135 | |
| 136 | This drops the three new tables. Any verification cache rows |
| 137 | are lost; the bulk backfill on the subsequent re-deploy |
| 138 | regenerates them. |
| 139 | |
| 140 | ## See also |
| 141 | |
| 142 | - User docs: [GPG keys & commit signing](/docs/public/user/gpg-keys.md) |
| 143 | - API reference: [GPG keys](/docs/public/api/gpg-keys.md), |
| 144 | [Signature verification on commits](/docs/public/api/commits.md#signature-verification) |