MySQL · 1975 bytes Raw Blame History
1 -- SPDX-License-Identifier: AGPL-3.0-or-later
2 --
3 -- Reverse-lookup index for OpenPGP subkey fingerprints. A commit's
4 -- signature packet carries the SIGNING SUBKEY's fingerprint (or key
5 -- id), not the primary's; the verification hot path needs a fast
6 -- (fingerprint → user_id) mapping that doesn't require parsing
7 -- every user_gpg_keys.armored block.
8 --
9 -- The same data is also serialized into user_gpg_keys.subkeys (JSONB)
10 -- so the REST response can nest subkeys under the primary without
11 -- a join; both representations are populated atomically when the
12 -- primary key is inserted.
13 --
14 -- Global uniqueness on fingerprint (partial, where revoked_at is null)
15 -- mirrors the primary table's policy.
16
17 -- +goose Up
18 CREATE TABLE user_gpg_subkeys (
19 id bigserial PRIMARY KEY,
20 gpg_key_id bigint NOT NULL REFERENCES user_gpg_keys(id) ON DELETE CASCADE,
21 fingerprint text NOT NULL,
22 key_id text NOT NULL,
23 can_sign boolean NOT NULL,
24 can_encrypt_comms boolean NOT NULL,
25 can_encrypt_storage boolean NOT NULL,
26 can_certify boolean NOT NULL,
27 expires_at timestamptz,
28 revoked_at timestamptz,
29 created_at timestamptz NOT NULL DEFAULT now(),
30
31 CONSTRAINT user_gpg_subkeys_fingerprint_format CHECK (fingerprint ~ '^[0-9a-f]{40}$'),
32 CONSTRAINT user_gpg_subkeys_key_id_format CHECK (key_id ~ '^[0-9a-f]{16}$')
33 );
34
35 CREATE UNIQUE INDEX user_gpg_subkeys_fingerprint_uniq
36 ON user_gpg_subkeys (fingerprint)
37 WHERE revoked_at IS NULL;
38
39 CREATE INDEX user_gpg_subkeys_key_id_idx ON user_gpg_subkeys (key_id);
40 CREATE INDEX user_gpg_subkeys_gpg_key_id_idx ON user_gpg_subkeys (gpg_key_id);
41
42 -- +goose Down
43 DROP INDEX IF EXISTS user_gpg_subkeys_gpg_key_id_idx;
44 DROP INDEX IF EXISTS user_gpg_subkeys_key_id_idx;
45 DROP INDEX IF EXISTS user_gpg_subkeys_fingerprint_uniq;
46 DROP TABLE IF EXISTS user_gpg_subkeys;
47