-- Migration 004: the Dex-subject → tapir-user map (ADR-012 Stage 1, multi-user). -- A Dex-authenticated subject is the login identity; the tapir user_id (UUID) is -- what every user-owned, force-RLS table keys off. This table is the bridge: -- resolve subject → user_id here (auth plumbing, pre-scope), THEN scope all data -- access by that id via the store's withUser helper. -- -- INTENTIONALLY NOT RLS-ENABLED. The forced-RLS isolation (migration 003) guards -- the user-OWNED data tables. user_identities holds no user data — only an opaque -- (dex_subject ↔ user_id) pair — and must be readable BEFORE a user_id is known -- (that lookup is what yields the id used to set tapir.current_user_id). Putting -- RLS here would be a chicken-and-egg deadlock (you'd need the GUC to read the row -- that tells you the GUC). Data isolation lives on the user-owned tables, not here. CREATE TABLE user_identities ( dex_subject TEXT PRIMARY KEY, user_id UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); COMMENT ON TABLE user_identities IS 'Dex subject -> tapir user_id map. Auth plumbing, deliberately NOT RLS-enabled ' '(no user data; must be read pre-scope to resolve the id used for RLS). ' 'ON DELETE CASCADE so deleting a user cleans up its identity mapping.';