Documentation pass — reviewed codebase in full, identified stale references, and aligned docs with actual code state. Changes: - CLAUDE.md, PRODUCTION_SETUP.md, LAUNCH_CHECKLIST.md, src/auth.config.ts: middleware path src/middleware.ts → src/proxy.ts (Next.js 16+ convention) - CLAUDE.md, ENVIRONMENT.md, README.md: dev server port localhost:3000 → localhost:4000 (per package.json dev script 'next dev --webpack -H 0.0.0.0 -p 4000') - PRODUCTION_DEPLOYMENT_CHECKLIST.md: rewrote migration list — actual db/migrations/ contains 10 files (0000–0091), not the Supabase-era 001–092 series. Updated platform version 1.6 → 2.0 and last-updated stamp. - MEMORY.md: refreshed 'Last updated' to 2026-06-25, added current-state header flagging Auth.js v5 wiring as historical, and documented the SSH/Gitea workflow (id_ed25519_crispygoat + no API token → push-hook URL flow for opening PRs). - LAUNCH_CHECKLIST.md: added 'Last updated: 2026-06-25' header and refresh note in footer. - CLAUDE.md: new 'Gitea authentication (SSH)' subsection under 'Canonical Remote' documenting ~/.ssh/id_ed25519_crispygoat, ssh-agent setup, what works (git push) vs what doesn't (REST API without token), and the actual PR-creation workflow (push + open the URL the Gitea hook prints). Verification: - npx tsc --noEmit: pre-existing Stripe API version + test mock errors only (verified by stashing changes and re-running — same error set). - No new errors introduced.
8.9 KiB
Environment Setup
This guide documents every environment variable the platform uses, what each one does, where to get it, and any gotchas.
Copy .env.example → .env.local and fill in the values.
Public Variables
These are safe to commit and can be used in client bundles. Prefix with NEXT_PUBLIC_.
NEXT_PUBLIC_BASE_URL
The base URL of your deployment. Used for OAuth redirects and webhook URLs.
- Local:
http://localhost:4000(dev script binds to port 4000; override vianpm run dev -- -p <port>) - Production:
https://yourdomain.com
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
Stripe publishable key for Stripe.js in the client.
- Where to get: Stripe Dashboard → Developers → API Keys → Publishable key
- Format:
pk_live_...orpk_test_...
NEXT_PUBLIC_SINGLE_BRAND
Set to a brand slug to hide other brand routes in single-brand mode.
- Example:
tuxedo - Default: empty (multi-brand mode)
Server-Only Variables
Never prefix with NEXT_PUBLIC_. These must only be accessed in Server Components, Server Actions, or API Routes.
Database
DATABASE_URL
Full Postgres connection string. The app uses this exclusively (no Supabase, no JS client) for every server-side query — server actions and API routes import a shared pg Pool from src/lib/db.ts and call SECURITY DEFINER PL/pgSQL functions.
- Where to get: Neon Dashboard → Project → Connection Details → Connection string (pooled)
- Example:
postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/routecommerce?sslmode=require - Format:
postgres://user:pass@host:port/dbname?sslmode=require
DATABASE_ADMIN_URL (optional)
Same connection string but with elevated perms (typically the direct, non-pooled Neon connection). Only used by scripts/migrate.js for DDL — fall back to DATABASE_URL if unset.
Stripe
STRIPE_SECRET_KEY
Stripe secret key for backend API calls (creating sessions, webhooks, etc.).
- Where to get: Stripe Dashboard → Developers → API Keys → Secret key
- Format:
sk_live_...orsk_test_...
STRIPE_WEBHOOK_SECRET
Signing secret for Stripe webhook payloads.
- Where to get: Stripe Dashboard → Developers → Webhooks → select endpoint → Signing secret
- Format:
whsec_...
Stripe Price IDs
Create these as recurring prices in your Stripe Dashboard first. Each maps to a plan tier or add-on.
| Variable | What it is | Stripe Dashboard |
|---|---|---|
STRIPE_PRICE_STARTER |
$49/mo Starter plan | Create a Product → Price at $49/mo |
STRIPE_PRICE_FARM |
$149/mo Farm plan | Create a Product → Price at $149/mo |
STRIPE_PRICE_ENTERPRISE |
$399/mo Enterprise plan | Create a Product → Price at $399/mo |
STRIPE_PRICE_HARVEST_REACH |
$79/mo Harvest Reach add-on | Create a Product → Price at $79/mo recurring |
STRIPE_PRICE_WHOLESALE_PORTAL |
$99/mo Wholesale Portal add-on | Create a Product → Price at $99/mo recurring |
STRIPE_PRICE_WATER_LOG |
$39/mo Water Log add-on | Create a Product → Price at $39/mo recurring |
STRIPE_PRICE_AI_TOOLS |
$59/mo AI Intelligence add-on | Create a Product → Price at $59/mo recurring |
STRIPE_PRICE_SQUARE_SYNC |
$39/mo Square Sync add-on | Create a Product → Price at $39/mo recurring |
STRIPE_PRICE_SMS_CAMPAIGNS |
$29/mo SMS Campaigns add-on | Create a Product → Price at $29/mo recurring |
Note: Annual pricing requires separate annual prices (e.g., $441/yr for Starter = $49 × 12 × 0.75). Use different price IDs and pass annual=true to checkout actions.
Email (Resend)
RESEND_API_KEY
API key for sending transactional email via Resend.
- Where to get: Resend.com → API Keys → Create API key
- Format:
re_...
RESEND_WEBHOOK_SECRET
Signing secret for Resend webhook payloads.
- Where to get: Resend Dashboard → Webhooks → select endpoint → Signing secret
- Format:
whsec_...
AI
The platform supports five AI providers. Each brand can override per-provider keys in the admin AI Provider panel (/admin/settings/ai). Env vars here are fallbacks for when no per-brand key is configured.
OPENAI_API_KEY
OpenAI API key for AI features (AI Intelligence Pack add-on).
- Where to get: OpenAI Platform → API Keys → Create secret key
- Format:
sk-...
OPENAI_ORG_ID
OpenAI organization ID (optional — for workspace-level usage tracking).
- Where to get: OpenAI Platform → Settings → Organization ID
- Format:
org-...
MINIMAX_API_KEY
MiniMax (MiniMax) API key — OpenAI-compatible. Pre-launch default provider.
- Where to get: MiniMax Platform → API Keys
- Format:
ey...(JWT-style) - Used by:
getAIClient()falls back to this when no per-brand key is set.ai-import.tsImport Center also prefers this overOPENAI_API_KEYwhen both are set.
MINIMAX_BASE_URL (optional)
Override the MiniMax API base URL.
- Default:
https://api.minimax.io/v1(global) - China:
https://api.minimaxi.com/v1(mainland endpoint, lower latency inside China)
Other providers
The admin panel also accepts per-brand keys for Anthropic (sk-ant-...), Google Gemini (AIza...), xAI (xai-...), and custom OpenAI-compatible endpoints. These can also be set globally via ANTHROPIC_API_KEY, GOOGLE_API_KEY, XAI_API_KEY.
Square
SQUARE_APP_SECRET
Square app secret for Square API authentication.
- Where to get: Square Developer Dashboard → Your App → Credentials → Application Secret
- Format:
sq0...
SQUARE_ENVIRONMENT
Controls whether to use Square sandbox or production.
- Values:
sandbox(default for dev) orproduction - ⚠️ Note: Production credentials and sandbox credentials are separate. Make sure this matches which credentials you're using.
Webhook Setup
Stripe
Dashboard: Stripe Dashboard → Developers → Webhooks → Add endpoint
- URL:
https://yourdomain.com/api/stripe/webhook - Events to listen for:
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
Resend
Dashboard: Resend → Webhooks → Add webhook
- URL:
https://yourdomain.com/api/resend/webhook - Events:
email_delivered,email_bounced,email_complained
Local vs Production
| Variable | Local (.env.local) |
Production (hosting dashboard) |
|---|---|---|
NEXT_PUBLIC_BASE_URL |
http://localhost:4000 (or whatever port the dev server is on) |
https://yourdomain.com |
STRIPE_SECRET_KEY |
sk_test_... |
sk_live_... |
STRIPE_PUBLISHABLE_KEY |
pk_test_... |
pk_live_... |
SQUARE_ENVIRONMENT |
sandbox |
production |
Gotchas
- Wrong Stripe key type: Using
sk_live_in development orsk_test_in production will silently fail. Always match key type to environment. - Stripe price IDs drift: If you create new prices in Stripe Dashboard but forget to update
.env.local, billing will fail. Keep them in sync. - SQUARE_ENVIRONMENT mismatch: Production Square credentials won't work with
sandbox. Match it to your app secret type. - OPENAI_API_KEY for AI features: AI features won't work without this. The AI Intelligence Pack add-on requires a valid OpenAI key.
Authentication
Production (HTTPS Required)
Neon Auth session cookies use the __Secure- prefix, which requires HTTPS. In production, the auth flow works as follows:
- User submits credentials to
/api/auth/sign-in - Neon Auth server sets session cookie with
secure: true - Browser stores the cookie and sends it with subsequent requests
- Middleware validates the session cookie
Local Development (HTTP)
For local development over HTTP (e.g., http://localhost:4000), the platform provides a dev_session bypass:
- Via Login Page: Visit
/login- you'll see "Dev Mode — Quick Access" buttons for Platform Admin, Brand Admin, and Store Employee roles. - Via Browser Console:
document.cookie = 'dev_session=platform_admin; path=/; max-age=86400' - Via curl:
curl -b "dev_session=platform_admin" http://localhost:4000/admin
The dev_session cookie is automatically recognized by:
- The middleware (
src/proxy.ts) - The
getAdminUser()function (src/lib/admin-permissions.ts)
Note: The dev_session bypass only works when NODE_ENV !== "production". In production, only Neon Auth session cookies are accepted.
HTTPS for Local Development
If you prefer to test with real auth over HTTPS locally, you can use a tool like mkcert:
# Install mkcert
brew install mkcert
# Create local CA and install it
mkcert -install
# Generate certificate for localhost
mkcert localhost 127.0.0.1 ::1
# Update next.config.ts to use HTTPS
For most development work, the dev_session bypass is sufficient.