-- +goose Up -- Self-serve forgot-password: durable hashed one-time reset tokens (not must_set_password / invites). CREATE TABLE password_reset_tokens ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash TEXT NOT NULL, expires_at TIMESTAMPTZ NOT NULL, consumed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT password_reset_tokens_token_hash_len CHECK (char_length(token_hash) = 64) ); CREATE UNIQUE INDEX password_reset_tokens_token_hash_uidx ON password_reset_tokens (token_hash); CREATE INDEX password_reset_tokens_user_pending_idx ON password_reset_tokens (user_id, expires_at DESC) WHERE consumed_at IS NULL; -- +goose Down DROP TABLE IF EXISTS password_reset_tokens;