@@ -0,0 +1,144 @@ |
| 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 | + - `0066_user_gpg_keys.sql` — primary keys table |
| 32 | + - `0067_user_gpg_subkeys.sql` — subkey fingerprint reverse-lookup |
| 33 | + - `0068_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) |