-- A key has to reach the person it was issued for, and a key pasted into a chat -- window stays in that chat window forever. A claim is a one-time link instead: -- the operator sends the link, the person opens it once, and the link dies. -- -- The key is not stored here in the clear. It is sealed with a key derived from -- the claim token, and the token is never written down — only its hash is. So a -- database leak yields two useless hashes and a blob nothing in the database can -- open, which keeps the property the api_keys table has. CREATE TABLE key_claims ( token_hash BLOB PRIMARY KEY, key_id TEXT NOT NULL REFERENCES api_keys(id), -- The sealed key. Emptied the moment it is claimed, so a claimed link leaves -- nothing behind even before the row is purged. sealed BLOB NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL, claimed_at INTEGER ); CREATE INDEX key_claims_by_key ON key_claims(key_id); CREATE INDEX key_claims_by_expiry ON key_claims(expires_at);