-- --------------------------------------------------------------- -- 022 — public_profiles (A1 Session 10, public ledger profiles v1). -- -- Stage-3 seed: a user may claim a handle and publish their ENTIRE settled -- ledger record on a public page — wins and misses. Strava for betting. -- -- PRIVACY: PRIVATE BY DEFAULT. `published` defaults false and only flips -- via one explicit toggle in Settings. The profile API returns the SAME -- 404 for an unknown handle and an unpublished one (no existence leak). -- -- All writes go through the service role (the /api/profiles/me route). -- Clients only read: published rows (anyone) + their own row (owner). -- --------------------------------------------------------------- CREATE TABLE IF NOT EXISTS public.public_profiles ( user_id uuid PRIMARY KEY REFERENCES auth.users (id) ON DELETE CASCADE, handle text UNIQUE NOT NULL CHECK (handle ~ '^[a-z0-9_]{3,20}$'), published boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now() ); ALTER TABLE public.public_profiles ENABLE ROW LEVEL SECURITY; -- Anyone (anon + authenticated) reads PUBLISHED profiles only. CREATE POLICY public_profiles_select_published ON public.public_profiles FOR SELECT TO anon, authenticated USING (published = true); -- The owner reads their own row (published or not). CREATE POLICY public_profiles_select_own ON public.public_profiles FOR SELECT TO authenticated USING (user_id = auth.uid()); -- No INSERT/UPDATE/DELETE policies: client-side writes are impossible. -- The API writes via the service role, which bypasses RLS.