-- instructors — replaces the single shared ADMIN_PASSWORD (Phase 2B) with real per-person -- accounts. password_hash is ":" from scrypt (see src/auth/password.js), -- NULL only if an instructor is ever external-auth-only (future OIDC, see auth_identities). -- -- Roles are a plain CHECK, not a lookup table — three roles, not expected to churn often, and -- a CHECK is enough to keep bad data out without a join on every permission check. CREATE TABLE IF NOT EXISTS instructors ( instructor_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT NOT NULL UNIQUE, -- always stored lowercased by the application display_name TEXT NOT NULL, password_hash TEXT, role TEXT NOT NULL DEFAULT 'instructor' CHECK (role IN ('owner', 'admin', 'instructor')), status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), last_login_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_instructors_role_status ON instructors(role, status);