The publishable key is public - plan for it
Anyone can read it out of your shipped frontend. That is by design. What protects your data:- Row-level security. RLS tables scope rows to the signed-in user automatically. Keep RLS on (the default) for any per-user data.
- Signed-in-only writes. Storage uploads reject anonymous callers.
- Functions as the trust boundary. AI calls and email sends work with the publishable key - anyone holding it can drive spend. Put expensive or sensitive operations in a serverless function.
The admin key is never public
- Never in client code, never in
NEXT_PUBLIC_*vars, never in the browser. - It belongs in
.env.localfor the CLI, or server-side code only. - Everything the CLI does (schema, buckets, secrets, payment keys) uses it - that’s why setup is a terminal activity.
Payments
Never fulfill an order from the success URL - anyone can open that URL. Verified webhook events land in thepayments.webhook_events table; drive
fulfillment from those rows. See Payments.