-- 000002_e2ee_chat.up.sql -- Signal Keys (if not in profiles) CREATE TABLE IF NOT EXISTS signal_keys ( user_id UUID PRIMARY KEY REFERENCES profiles(id) ON DELETE CASCADE, identity_key_public TEXT NOT NULL, signed_prekey_public TEXT NOT NULL, signed_prekey_id INTEGER NOT NULL DEFAULT 1, signed_prekey_signature TEXT NOT NULL, one_time_prekeys JSONB DEFAULT '[]'::JSONB, registration_id INTEGER, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Encrypted Conversations CREATE TABLE IF NOT EXISTS encrypted_conversations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), participant_a UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, participant_b UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, created_at TIMESTAMPTZ DEFAULT NOW(), last_message_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT ordered_participants CHECK (participant_a < participant_b), CONSTRAINT unique_conversation UNIQUE (participant_a, participant_b) ); -- Encrypted Messages CREATE TABLE IF NOT EXISTS encrypted_messages ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), conversation_id UUID NOT NULL REFERENCES encrypted_conversations(id) ON DELETE CASCADE, sender_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, ciphertext BYTEA NOT NULL, message_header TEXT NOT NULL, message_type INTEGER NOT NULL DEFAULT 2, created_at TIMESTAMPTZ DEFAULT NOW(), delivered_at TIMESTAMPTZ, read_at TIMESTAMPTZ, expires_at TIMESTAMPTZ ); -- E2EE Session State (Compatibility table) CREATE TABLE IF NOT EXISTS e2ee_session_state ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, peer_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, user_has_session BOOLEAN DEFAULT FALSE, peer_has_session BOOLEAN DEFAULT FALSE, user_session_created_at TIMESTAMPTZ, peer_session_created_at TIMESTAMPTZ, user_session_version INTEGER DEFAULT 1, peer_session_version INTEGER DEFAULT 1, session_data JSONB, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (user_id, peer_id) ); -- One Time Prekeys (Compatibility table) CREATE TABLE IF NOT EXISTS one_time_prekeys ( id BIGSERIAL PRIMARY KEY, user_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, key_id INTEGER NOT NULL, public_key TEXT NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (user_id, key_id) ); -- INDEXES CREATE INDEX IF NOT EXISTS idx_conversations_participants ON encrypted_conversations(participant_a, participant_b); CREATE INDEX IF NOT EXISTS idx_messages_conversation ON encrypted_messages(conversation_id);