Security
Security work happens at three layers: the database, the application, and the network.
Database
- Row-Level Security (RLS). Every public table has explicit RLS policies. The default is "deny", and policies grant access scoped to
auth.uid(). - Security definer functions. Privileged checks like
has_role,user_has_premium_prompt_access, andis_pack_folderrun as the function owner with a lockedsearch_path, so the policy logic cannot be subverted by altering the search path. - Audit log. The
audit_trigger_functionrecords sensitive operations with PII and credentials stripped. Records are purged on a 90-day schedule viapg_cron. - Rate limiting. A
rate_limitstable blocks repeated abusive operations with progressive backoff. Records retain for 90 days.
Application
- Ownership checks use the authenticated
user.idon every mutation, never values from the browser. - Edit and delete UIs are hidden when ownership does not match, and the API still rejects requests that bypass the UI.
- Frontend logging is gated behind
import.meta.env.DEVso production builds never log PII. - Edge functions use a
safeLogginghelper that masks emails, tokens, and IDs before writing to logs. - Custom email templates are signed by Supabase's auth service. Magic links and password resets expire quickly.
Network
- HTTPS everywhere. Custom domains automatically receive SSL through Cloudflare.
- Strict CSP on the main app restricts script sources to Supabase, Stripe, and the affiliate tracker.
- Realtime channels are multiplexed per user and authenticated on subscription.
Payments
- Stripe processes every charge. PromptsVault never sees full card numbers.
- Webhook signatures are verified server-side before any database write.
Reporting an issue
If you find a security issue, please email security@promptsvault.app. Do not file a public GitHub issue. Coordinated disclosure helps protect every user.