max / makenotwork
| 1 | -- Store invite codes hashed, never in plaintext (audit 2026-07-01): the lone |
| 2 | -- single-use credential that was readable at rest. A DB read must not yield a |
| 3 | -- directly-usable invite, matching the reset/login/git-PAT posture. |
| 4 | -- |
| 5 | -- Widen the column first (a SHA-256 hex digest is 64 chars, the old cap was 12), |
| 6 | -- then hash existing plaintext codes in place so outstanding invites keep |
| 7 | -- working. `encode(sha256(...))` yields lowercase hex, matching the Rust |
| 8 | -- `crypto::invite_code_hash` output. |
| 9 | |
| 10 | invite_codes ALTER COLUMN code TYPE TEXT; |
| 11 | |
| 12 | UPDATE invite_codes |
| 13 | SET code = encode(sha256(code::bytea), 'hex') |
| 14 | -- A 64-char lowercase-hex value is already a hash; only rewrite plaintext rows |
| 15 | -- so the migration is idempotent if partially applied. |
| 16 | WHERE code !~ '^[0-9a-f]{64}$'; |
| 17 |