Mental model
Vibestrap follows a fill-what-you-use model. The app boots with two real values (DATABASE_URL + BETTER_AUTH_SECRET); everything else is optional and gracefully
no-ops when blank. Don’t have a Stripe account yet? The pricing page just hides the
checkout button. No Resend key? Password reset still renders, just doesn’t send.
But here’s the trap most people fall into:
The 3 config layers
Vibestrap’s configuration lives in three places, each for a reason:| Layer | Lives in | What it controls |
|---|---|---|
| Brand & product | src/config/site.ts | Name, copy, pricing display, social links |
| Translations | messages/{en,zh}.json | All user-facing strings |
| Secrets & toggles | k8s/.env (or .env.local for dev) | API keys, DB URLs, provider switches |
Where you’ll edit values
The flow on a live cluster:.env.local and pnpm dev picks them up
automatically — no restart dance.
Required: the 4 values without which the site shows 500
Skip any of these and the site won’t render. Do these first.DATABASE_URL — your Postgres
/login,
/register, /pricing, /admin will not.Where to sign up: neon.tech — serverless Postgres, generous
free tier, indie-friendly. (Alternatives: Supabase, Railway, RDS — anything that
speaks Postgres 15+.)Create the project
- Sign up at https://neon.tech
- Create a new project — pick a region close to your K8s cluster (e.g.
us-east-2if your nodes are there) - Wait ~10 seconds for provisioning to finish
Grab the connection string
- In the left sidebar, click Connection Details
- Important: select Pooled connection (handles serverless and burst traffic way better than direct)
- Copy the URL it shows
- Make sure it ends with
?sslmode=require— Neon enforces TLS, your app will fail to connect without it
| Variable | Example |
|---|---|
DATABASE_URL | postgres://neondb_owner:...@ep-xxx-pooler.us-east-2.aws.neon.tech/neondb?sslmode=require |
BETTER_AUTH_SECRET — auth signing key
| Variable | Example |
|---|---|
BETTER_AUTH_SECRET | S8sFO3lhBaqfDD0p308BqjBNQ6TssHA9i2p4JoMYSb4= |
NEXT_PUBLIC_APP_URL — your domain
localhost, every shared link will be broken.| Variable | Example |
|---|---|
NEXT_PUBLIC_APP_URL | https://vibestrap.dev |
http://.BETTER_AUTH_URL — auth callback root
NEXT_PUBLIC_APP_URL. The only reason to
split them is if your auth lives on a different subdomain.| Variable | Example |
|---|---|
BETTER_AUTH_URL | https://vibestrap.dev |
Strongly recommended: the things buyers expect to work
Without these, the site loads but key features are broken: users can’t recover passwords, can’t sign in via Google or GitHub, can’t pay you. Set them up before launch — not after the first user complaint.Resend (transactional email)
Why: password reset, email verification, welcome emails. Without it, the “forgot password” button is purely decorative. Where to sign up: resend.com — free tier covers 100 emails per day, 3,000 per month. Plenty for early launch.Add and verify your domain
- Left sidebar → Domains → Add Domain
- Enter your domain (e.g.
vibestrap.dev) - Resend shows 3 DNS records (SPF, DKIM, DMARC) — don’t close the page
- Add those DNS records at your registrar (Cloudflare, Namecheap, GoDaddy, …)
- Click Verify DNS Records; wait 5–30 min until status shows Verified
| Variable | Where it comes from | Example |
|---|---|---|
RESEND_API_KEY | Resend → API Keys | re_a1b2c3d4... |
RESEND_FROM_EMAIL | Any email at your verified domain | Larry from Vibestrap <hello@vibestrap.dev> |
RESEND_REPLY_TO_EMAIL | Inbox you actually read | larry@vibestrap.dev |
RESEND_FROM_EMAIL (e.g. Larry from Vibestrap) is
optional, but it noticeably improves deliverability — bare hello@… looks like a bot.Admin access
You’ll want to reach/admin yourself.
| Variable | Example |
|---|---|
ADMIN_EMAILS | wisehackerlarry@gmail.com,partner@example.com |
admin role on first login and from then on can access /admin.
Google OAuth
Why: roughly 60% of indie devs prefer “sign in with Google” over typing a password. The conversion lift on a signup form with this button alone is huge. Where to set up: console.cloud.google.comConfigure the OAuth consent screen
- Left menu → APIs & Services → OAuth consent screen
- User type: External → Create
- Fill App name, User support email, Developer contact email
- Save and continue through the scopes / test users screens (defaults are fine)
Create the OAuth client
- Left menu → APIs & Services → Credentials
- + Create Credentials → OAuth 2.0 Client ID
- Application type: Web application
- Name: anything (e.g. “Vibestrap”)
- Authorized JavaScript origins:
https://vibestrap.dev - Authorized redirect URIs:
https://vibestrap.dev/api/auth/callback/google(this exact path — Better Auth uses it) - Click Create → modal shows Client ID + Client Secret → copy both immediately
| Variable | Example |
|---|---|
GOOGLE_CLIENT_ID | 1234567890-abc...apps.googleusercontent.com |
GOOGLE_CLIENT_SECRET | GOCSPX-abcdef... |
NEXT_PUBLIC_GOOGLE_CLIENT_ID | same as GOOGLE_CLIENT_ID (mirror, used for client-side One-Tap) |
GitHub OAuth
Why: Vibestrap targets developers, and “sign in with GitHub” converts ~80% of devs who land on your signup page. Worth 5 minutes of setup. Where to set up: github.com/settings/developers → OAuth Apps → New OAuth AppFill the form
- Application name: Vibestrap (or whatever you want users to see)
- Homepage URL:
https://vibestrap.dev - Authorization callback URL:
https://vibestrap.dev/api/auth/callback/github - Click Register application
| Variable | Example |
|---|---|
GITHUB_CLIENT_ID | Iv1.abc123... |
GITHUB_CLIENT_SECRET | ghs_abcdef... |
Stripe
Why: take money. Without this, the entire pricing page is decorative. Where to set up: dashboard.stripe.comActivate your Stripe account
Grab the API keys
- Developers → API keys
- Copy Secret key (
sk_live_...) - Copy Publishable key (
pk_live_...)
Create the product and prices
- Products → + Add product
- Name it “Vibestrap” (or whatever your product is called)
- Add prices under that product. For Vibestrap typically two:
- Standard one-time: e.g. $99
- Promo one-time: e.g. $49 (optional sale price)
- Click into each price; copy the
price_xxxID from the URL or the price detail panel
Register the webhook
- Developers → Webhooks → + Add endpoint
- Endpoint URL:
https://vibestrap.dev/api/webhooks/stripe - Events to send (toggle each):
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
- Click Add endpoint
- On the endpoint detail page, click Reveal next to Signing secret → copy
whsec_...
| Variable | Example |
|---|---|
STRIPE_SECRET_KEY | sk_live_abc... |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | pk_live_abc... |
STRIPE_WEBHOOK_SECRET | whsec_abc... |
STRIPE_PRICE_VIBESTRAP_STANDARD | price_abc... (your standard SKU) |
STRIPE_PRICE_VIBESTRAP_PROMO | price_abc... (optional, for sale price) |
SEO + analytics — strongly recommended
These don’t affect whether your site works, but they directly affect whether anyone finds it.Google Search Console — site verification
Why: Google won’t reliably index your site without verification. No SEO traffic = no organic growth = paid ads are your only acquisition channel. Where: search.google.com/search-consoleAdd the property
- Add property → URL prefix → enter
https://vibestrap.dev - Choose HTML tag verification method
| Variable | Example |
|---|---|
GOOGLE_SITE_VERIFICATION | Rj7vQ... (the content="..." value, not the full tag) |
Microsoft Clarity — heatmaps + session recordings
Why: free heatmaps, session recordings, and dead-click detection. The best $0 you’ll ever not-spend on user research. Where: clarity.microsoft.com| Variable | Example |
|---|---|
NEXT_PUBLIC_CLARITY_PROJECT_ID | abc123xyz0 |
PostHog — product analytics (optional)
Why: funnels, retention, feature flags. Free tier covers 1M events/month, more than enough for early-stage products. Where: posthog.com| Variable | Example |
|---|---|
NEXT_PUBLIC_POSTHOG_KEY | phc_abc... |
NEXT_PUBLIC_POSTHOG_HOST | https://us.i.posthog.com (default) |
Optional — only when you actually need them
A single table for everything else. Don’t pre-emptively wire these — they’re each “feature off until you fill in the var”.| Feature | Set up when | Variables | Where to sign up |
|---|---|---|---|
| Bing site verification | You want Bing/DuckDuckGo traffic | BING_SITE_VERIFICATION | bing.com/webmasters |
| Yandex verification | Targeting Russian-speaking users | YANDEX_VERIFICATION | webmaster.yandex.com |
| Resend Audience newsletter | Collecting email subs via Resend | RESEND_AUDIENCE_ID | resend.com → Audiences |
| Beehiiv newsletter | You already use Beehiiv | BEEHIIV_API_KEY, BEEHIIV_PUBLICATION_ID | beehiiv.com |
| Crisp chat | Want live chat widget | NEXT_PUBLIC_CRISP_WEBSITE_ID | crisp.chat |
| Tawk.to chat | Free Crisp alternative | NEXT_PUBLIC_TAWK_PROPERTY_ID, NEXT_PUBLIC_TAWK_WIDGET_ID | tawk.to |
| Intercom | Bigger budget, better CRM | NEXT_PUBLIC_INTERCOM_APP_ID | intercom.com |
| Chatwoot | Self-hosted support | NEXT_PUBLIC_CHATWOOT_WEBSITE_TOKEN, NEXT_PUBLIC_CHATWOOT_BASE_URL | chatwoot.com |
| Cloudflare Turnstile | Anti-bot on signup/checkout | NEXT_PUBLIC_TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY | cloudflare.com |
| Affonso affiliates | Affiliate program | NEXT_PUBLIC_AFFONSO_ID | affonso.io |
| Rewardful affiliates | Stripe-native affiliates | NEXT_PUBLIC_REWARDFUL_API_KEY | rewardful.com |
| Creem (alt to Stripe) | Stripe unavailable in your region | CREEM_API_KEY, CREEM_WEBHOOK_SECRET, CREEM_PRODUCT_ID | creem.io |
| AI providers | Your product uses AI | AI_PROVIDER, OPENAI_API_KEY, ANTHROPIC_API_KEY, OPENROUTER_API_KEY, REPLICATE_API_TOKEN, FAL_KEY | provider websites |
| S3 / R2 storage | User uploads, generated images | S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | AWS S3 or Cloudflare R2 |
| GitHub invite | Inviting buyers to your private source repo | GITHUB_INVITE_TOKEN | fine-grained PAT — see GitHub invite delivery |
Apply + verify
You’ve filled ink8s/.env. Now push it into the cluster and prove it works.
Apply your changes
Verify the user flows (don’t trust pod status alone)
kubectl get pods showing 1/1 Running only proves the process boots. It does not
prove the database is reachable, OAuth redirects work, or Stripe webhooks fire. Walk
through this list manually:
Homepage renders
https://vibestrap.dev. Should render. If it 500s, the most common causes are
typoed DATABASE_URL, or you forgot to run pnpm db:migrate after
configuring the database (see the warning at the top of the
“Required” section).Email signup works
/register, sign up with email. Check your inbox for the welcome email.
Validates: Resend API key + verified domain.Google sign-in works
/login, click Continue with Google. Should redirect, prompt, return to
your app logged in. Validates: Google OAuth client + redirect URI.Forgot-password email arrives
/login, click “Forgot password”. Submit your email. Email should arrive within
30 seconds. Validates: Resend further + auth wiring.Admin dashboard reachable
ADMIN_EMAILS. Visit /admin. Should load — not 403.
Validates: ADMIN_EMAILS parsing.Pricing page → Stripe Checkout
/pricing. Click any checkout button. Should land on a Stripe Checkout page
under checkout.stripe.com. Validates: Stripe live keys + price IDs are configured.A real test purchase end-to-end
4242 4242 4242 4242 (only works
in test mode — for live mode use a real card and refund yourself, or run this whole
test against a sandbox first). Confirm:- payment shows up in Stripe dashboard
- webhook fires (Stripe → Webhooks → your endpoint shows a
200) - a row appears in
paymenttable - credits land in your account (visible in
/dashboard) - visiting
/settings/purchasesshows the GitHub invite form for the buyer-only repo