How Stadium Deck protects your data: row-level security coverage, storage isolation, and upload validation rules.
| Table | Scope | Read | Insert | Update | Delete | Policy |
|---|---|---|---|---|---|---|
| profiles | Owner-only (auth.uid() = id) | Owner | Owner | Owner | — | supabase/migrations/20260511184419_*.sql |
| user_library | Owner-only (auth.uid() = user_id) | Owner | Owner | Owner | Owner | supabase/migrations/20260511185043_*.sql |
| storage.objects (avatars bucket) | Owner folder ((storage.foldername(name))[1] = auth.uid()) | Owner | Owner | Owner | Owner | supabase/migrations/20260511195325_*.sql |
The avatars bucket is private. Avatars are never served from a permanent public URL — every render goes through a fresh signed URL that expires after one hour.
{auth.uid()}/avatar-…; storage RLS rejects writes whose first folder isn't the caller's user id.supabase.storage.from("avatars").createSignedUrl(path, 60 * 60) — a 3,600-second TTL.profiles.avatar_url (which stores the storage path, not a URL).403 and cannot be re-shared.No public URL methods are used anywhere in the codebase. A repo-wide search for getPublicUrl returns zero matches, and the bucket itself is configured with public = false.
Content-Security-Policy
Restricts script, style, image, and connect origins. Only self + Supabase APIs allowed for data; framing fully blocked.
default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: data:; connect-src 'self' https://*.supabase.co wss://*.supabase.co; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
X-Frame-Options
Legacy clickjacking guard. Mirrors CSP frame-ancestors 'none'.
DENY
Referrer-Policy
Sends only the origin to cross-site requests; full URL kept for same-origin navigation.
strict-origin-when-cross-origin
X-Content-Type-Options
Disables MIME sniffing so browsers respect declared content types.
nosniff
Permissions-Policy
Denies access to camera, microphone, geolocation, and payment APIs.
camera=(), microphone=(), geolocation=(), payment=()
No events recorded.
Auth, storage, and upload validation failures will appear here with timestamps.
/login with a return-path banner; signed-in users on /login bounce home.