tenseleyflow/shithub / eef73f8

Browse files

docs/api/commits: document verification object + reason enum

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
eef73f841364a05a5e68b37321e4eef4b96081e8
Parents
63d3b07
Tree
b2b19b5

1 changed file

StatusFile+-
M docs/public/api/commits.md 62 0
docs/public/api/commits.mdmodified
@@ -41,10 +41,21 @@ Response shape (one entry):
4141
     "name":  "Alice",
4242
     "email": "alice@example.com",
4343
     "date":  "2026-05-12T00:00:00Z"
44
+  },
45
+  "verification": {
46
+    "verified":    false,
47
+    "reason":      "unsigned",
48
+    "signature":   null,
49
+    "payload":     null,
50
+    "verified_at": null
4451
   }
4552
 }
4653
 ```
4754
 
55
+The `verification` object mirrors GitHub's documented shape and
56
+is always present. See [Signature verification](#signature-verification)
57
+below for the field semantics and the `reason` enum.
58
+
4859
 Empty / uninitialised repos return `[]` rather than `404`.
4960
 
5061
 ## Get a commit
@@ -82,3 +93,54 @@ that `git rev-parse` resolves. Unknown SHAs `404`.
8293
 `files[].status` is git's letter code (`A` added, `M` modified,
8394
 `D` deleted, `R` renamed, `C` copied, `T` type-changed). Renames
8495
 and copies surface the original path on `old_path`.
96
+
97
+## Signature verification
98
+
99
+Every commit response carries a `verification` object. shithub
100
+runs the signature check server-side against the bytes git
101
+stored in the commit object; the result is cached per
102
+`(repo, commit_oid)` and surfaced here.
103
+
104
+```json
105
+{
106
+  "verified":    true,
107
+  "reason":      "valid",
108
+  "signature":   "-----BEGIN PGP SIGNATURE-----\n…",
109
+  "payload":     "tree abc…\nparent def…\n…",
110
+  "verified_at": "2026-05-12T04:00:00Z"
111
+}
112
+```
113
+
114
+| Field         | Type             | Notes                                                                          |
115
+|---------------|------------------|--------------------------------------------------------------------------------|
116
+| `verified`    | bool             | `true` only when `reason == "valid"`. Always `false` otherwise.                |
117
+| `reason`      | string           | One of the enum values below.                                                  |
118
+| `signature`   | string \| null   | The armored signature block as stored on the commit object.                    |
119
+| `payload`     | string \| null   | The bytes the signature was computed over (commit object minus `gpgsig`).      |
120
+| `verified_at` | string \| null   | RFC3339 timestamp of the cache row; `null` for unsigned / not-yet-stamped.     |
121
+
122
+### `reason` enum
123
+
124
+The values mirror gh's documented enum exactly:
125
+
126
+| Value                 | Meaning                                                                                                  |
127
+|-----------------------|----------------------------------------------------------------------------------------------------------|
128
+| `valid`               | Signature parsed, cryptographically valid, signing email matches a verified email on the key's account.  |
129
+| `unsigned`            | Commit object carried no signature header. Default for cache misses; clients render no badge.            |
130
+| `unknown_key`         | Signature parsed but no uploaded GPG key matches the signing subkey's fingerprint.                       |
131
+| `unverified_email`    | Signature is valid for an uploaded key, but the signing email isn't verified on that key's account.      |
132
+| `bad_email`           | Signature is valid for an uploaded key, but the signing email isn't on the key at all.                   |
133
+| `expired_key`         | Signature parsed but the key was expired at signing time.                                                |
134
+| `not_signing_key`     | The key referenced isn't a signing key (capability bits missing).                                        |
135
+| `malformed_signature` | The `gpgsig` header didn't parse as an OpenPGP signature block.                                          |
136
+| `invalid`             | Signature parsed but the cryptographic check failed.                                                     |
137
+
138
+### Cache freshness
139
+
140
+Verification rows are populated by an asynchronous backfill that
141
+runs whenever a user uploads a GPG key (and once-off at deploy
142
+time via `shithubd gpg-backfill-all`). Between key upload and
143
+backfill completion, affected commits report `unsigned` (the
144
+conservative default); the badge appears once the row is
145
+stamped. Revoking a key invalidates affected cache rows;
146
+clients see `unsigned` until another matching key is uploaded.