Skip to main content

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, and is_pack_folder run as the function owner with a locked search_path, so the policy logic cannot be subverted by altering the search path.
  • Audit log. The audit_trigger_function records sensitive operations with PII and credentials stripped. Records are purged on a 90-day schedule via pg_cron.
  • Rate limiting. A rate_limits table blocks repeated abusive operations with progressive backoff. Records retain for 90 days.

Application

  • Ownership checks use the authenticated user.id on 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.DEV so production builds never log PII.
  • Edge functions use a safeLogging helper 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.