max / goingson
| 1 | -- The invitation half of the group-admin queue. |
| 2 | -- |
| 3 | -- Migration 070 queued `create_group`, `add_member` and `remove_member` and |
| 4 | -- said in its own header that the kind column is not a CHECK constraint, so |
| 5 | -- five new kinds need no change to it. What they do need is somewhere to put |
| 6 | -- what they carry, and none of 070's four payload columns fits: an invitation |
| 7 | -- is addressed by its own id, a pasted code is neither a name nor a public key, |
| 8 | -- and an expiry is a number. |
| 9 | -- |
| 10 | -- THE FIVE KINDS. `create_invite` reads `group_id` and `expires_in_hours`; |
| 11 | -- `revoke_invite` and `confirm_invite` read `group_id` and `invitation_id`; |
| 12 | -- `preview_invite` and `accept_invite` read `invite_token`. Columns rather than |
| 13 | -- a JSON blob, for migration 070's reason and by its precedent. |
| 14 | -- |
| 15 | -- `invite_token` IS NOT THE ISSUED TOKEN, and the distinction is the reason |
| 16 | -- this comment is longer than the DDL. An invitation's code is returned once by |
| 17 | -- `create_invitation` and the server keeps only a hash, so the only copy that |
| 18 | -- will ever exist belongs in `sync_invitations.token`, written by |
| 19 | -- `directory::record_issued` at the moment the drainer receives it. It is never |
| 20 | -- a column here: a queue row is swept an hour after it lands, and sweeping the |
| 21 | -- only copy of a live invite code would issue an invitation nobody can use. |
| 22 | -- |
| 23 | -- What this column holds is the opposite direction: a code the user was handed |
| 24 | -- by somebody else and pasted in, which they are holding anyway and which the |
| 25 | -- drainer has to be able to read a minute later. Nothing on this device is the |
| 26 | -- authority on it. |
| 27 | -- |
| 28 | -- CONFIRM STORES NO FINGERPRINT. `confirm_invite` carries the group and the |
| 29 | -- invitation and stops there, so the drainer re-reads the server's current |
| 30 | -- answer rather than acting on a copy taken at queue time. That is how |
| 31 | -- `remove_member` is already stored, and here it is stronger than consistency: |
| 32 | -- the fingerprint is the thing being authorized, and authorizing a value copied |
| 33 | -- a minute ago is exactly the substitution the out-of-band comparison exists to |
| 34 | -- catch. The screen still names the fingerprint on the queued row -- it reads |
| 35 | -- it from the directory when it draws, which is a display and not a claim. |
| 36 | -- |
| 37 | -- STILL LOCAL-ONLY, still absent from `syncstore::manifest`, and still in |
| 38 | -- `EXCLUDED_TABLES`. Adding a column to a table a backup does not carry needs |
| 39 | -- no registration on either side, and the storage version does not move: |
| 40 | -- nothing here crosses the wire. |
| 41 | |
| 42 | group_admin_queue ADD COLUMN invitation_id TEXT; |
| 43 | group_admin_queue ADD COLUMN invite_token TEXT; |
| 44 | group_admin_queue ADD COLUMN expires_in_hours INTEGER; |
| 45 |