ShipVeryFastShipVeryFast
Documentation

FAQ

Quick answers to the questions developers ask most when they first clone ShipVeryFast, package manager, which services you really need, env vars, Stripe modes, and where the database lives.

Required environment variables are validated at import time by libs/config.ts. If one is missing or malformed, the app throws on startup rather than failing silently later, so a clean boot means your config is valid.

Which package manager and Node version does this use?

ShipVeryFast uses npm. The repo ships a package-lock.json, and every script in the docs is written as npm run …. The install step also runs Husky git hooks via the prepare script (husky install), so a plain npm install sets up everything in one go.

git clone <your-repo-url> shipveryfast
cd shipveryfast
npm install          # also runs "husky install" via the prepare script
cp .env.example .env.local
npm run dev          # Next.js + Turbopack at http://localhost:3000

It is a Next.js 15 (App Router) + React 19 project, so any actively supported Node LTS release works. The dev server runs with Turbopack (next dev --turbopack) and serves on http://localhost:3000.

Can I run it without Stripe, Supabase, or AI keys?

Partly. The two behave very differently:

  • AI keys are fully optional. ANTHROPIC_API_KEY and OPENAI_API_KEY are declared as z.string().optional() in libs/config.ts. The AI assistant degrades gracefully, availableProviders() in libs/ai/index.ts only surfaces providers whose key is present, and the chat endpoint returns a 503 with a clear message if you call an unconfigured one. The rest of the app runs normally.
  • Supabase, Stripe, Mailgun and Google keys are required to boot. They are non-optional in the Zod schema, so the app throws at startup if they are missing. You can point them at free / test-tier accounts, that is enough to build and test auth, checkout and email end to end, but you cannot leave them blank and expect the server to start.

In short: you can run without spending money (test/free tiers), but not without the keys themselves. The AI layer is the only piece that truly runs with nothing configured.

Which env vars are truly required vs optional?

The source of truth is the envSchema in libs/config.ts, which calls envSchema.parse(process.env) at import time. Anything not marked .optional() there is required. Here is the snapshot.

Required vs optional env at a glance

VariableRequired?Zod rule / notes
SUPABASE_URLRequiredz.string().url()
SUPABASE_ANON_KEYRequiredz.string()
SUPABASE_SERVICE_ROLE_KEYRequiredz.string() · server-only secret
NEXTAUTH_SECRETRequiredz.string() · e.g. openssl rand -base64 32
NEXTAUTH_URLRequiredz.string().url() · set to your deployed URL in prod
STRIPE_SECRET_KEYRequiredz.string()
STRIPE_PUBLIC_KEYRequiredz.string()
STRIPE_WEBHOOK_SECRETRequiredz.string()
STRIPE_PRICE_BASICRequiredz.string()
STRIPE_PRICE_PRORequiredz.string()
STRIPE_PRICE_ENTERPRISEOptionalz.string().optional() · "contact sales" tier
MAILGUN_API_KEYRequiredz.string()
MAILGUN_DOMAINRequiredz.string()
MAILGUN_FROM_EMAILRequiredz.string().email()
GOOGLE_CLIENT_IDRequiredz.string() · Google OAuth
GOOGLE_CLIENT_SECRETRequiredz.string() · Google OAuth
RATE_LIMIT_MAGICLINK_MAXRequiredz.coerce.number() · default 5
RATE_LIMIT_MAGICLINK_DURATIONRequiredz.coerce.number() · default 3600 (seconds)
ANTHROPIC_API_KEYOptionalz.string().optional() · AI assistant (Claude)
OPENAI_API_KEYOptionalz.string().optional() · AI assistant (OpenAI)

Two more keys appear in .env.example but are not in the Zod schema, so they are not validated at startup: CSRF_SECRET (used by CSRF protection) and ADMIN_EMAILS. Set them anyway, they gate real behavior. For the full annotated list, see Environment variables.

How do I switch Stripe from test to live mode?

Nothing in the code is hard-coded to a mode. Stripe is "in" test or live purely based on which keys and price IDs you put in the environment. To go live, swap four kinds of values in your production environment:

  1. Replace your test STRIPE_SECRET_KEY and STRIPE_PUBLIC_KEY with the live keys from the Stripe Dashboard (Developers → API keys).
  2. Recreate (or copy) your products/prices in live mode and update STRIPE_PRICE_BASIC, STRIPE_PRICE_PRO and (if used) STRIPE_PRICE_ENTERPRISE with the live price IDs. These resolve through getStripePriceId() in libs/pricingPlans.ts, so changing the env is all it takes, no code edits.
  3. Create a live webhook endpoint pointing at your deployed app and set STRIPE_WEBHOOK_SECRETto that endpoint's signing secret. Test-mode and live-mode webhook secrets are different.
  4. Make sure NEXTAUTH_URL is your real production URL so Checkout redirects and webhook origins line up.

While developing, keep using Stripe's test card 4242 4242 4242 4242 with any future expiry and any CVC. See Payments for the full Checkout and webhook flow.

How do I add a provider, model, dashboard page, or feature flag?

Add an AI model or provider

The AI layer lives in libs/ai/. Each provider (e.g. libs/ai/anthropic.ts) exports its model catalogue. To add a model to an existing provider, add an entry to its MODELS array, the streaming chat endpoint and the picker read it automatically:

// libs/ai/anthropic.ts
const MODELS = [
  { id: 'claude-opus-4-8', label: 'Claude Opus 4.8' },
  { id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6' },
  { id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5' },
  // add your new model id + label here
];

To add a whole new provider, create a module that satisfies the Provider interface (an isConfigured() check plus an async streamChat() generator) and register it in the PROVIDERS map in libs/ai/index.ts. Because the UI is driven by providerCatalogue() / availableProviders(), it appears automatically once its key is set. See AI engine for the full contract.

Add a dashboard page

Dashboard navigation is data-driven in libs/dashboard/nav.ts. Create your route under app/dashboard/your-page/page.tsx, then add a NavItem to the relevant group in NAV_GROUPS (and a label in ROUTE_LABELS for breadcrumbs):

// libs/dashboard/nav.ts
{ href: '/dashboard/reports', label: 'Reports', icon: BarChart3 },

The sidebar, command palette and breadcrumbs all read from this config, so one entry wires the page into the whole shell. More in Dashboard.

Add a feature flag

Feature flags are exposed through the FeatureFlagsProvider. Read them with the useFeatureFlags() hook (note the plural), which returns isEnabled and loading:

import { useFeatureFlags } from '@/components/providers/FeatureFlagsProvider';

const { isEnabled, loading } = useFeatureFlags();
return isEnabled('new-billing') ? <NewBilling /> : <OldBilling />;

The admin dashboard at /admin also ships a visual feature flags manager. See Feature flags for managing flags and rollouts.

Where do migrations and SQL live, and how do I run the test DB?

SQL migrations live in supabase/migrations/. The boilerplate ships a single initial migration, 0001_initial_schema.sql, which creates the core tables: users, subscriptions, payments, email_verifications, password_resets and blog_posts (with Row Level Security). Apply it from the Supabase dashboard SQL editor or the Supabase CLI.

For local integration testing there is a throwaway Postgres container defined in docker-compose.yml (service db-test, postgres:15-alpine). It maps host port 5433 to container 5432, database shipveryfast_test, with user/password test/test:

npm run db:test:up     # docker-compose up -d db-test  (port 5433)
npm run test:integration
npm run db:test:down   # docker-compose down

For schema details and the typed Zod models that mirror these tables, see Database.

Next steps