Configuration
Reference for siteConfig, legalConfig and the environment variables validated by lib/env.ts.
Launch Now keeps product identity, legal details and secrets in three places: siteConfig for the product itself, legalConfig for the legal documents, and environment variables validated by lib/env.ts. This page lists every field, its type and default, and the code that reads it.
| File | Export | Purpose |
|---|---|---|
lib/config/site-config.ts | siteConfig | Product name, URLs, metadata, social links. |
lib/config/legal.ts | legalConfig, findLegalPlaceholders(), types LegalConfig, CookieEntry, CookieCategory, Subprocessor | Company details and settings used to build the /legal documents and the cookie banner. |
lib/env.ts | env | Type-safe, validated environment variables. |
siteConfig
siteConfig is the single source of truth for the product identity. The demo values describe Pulse, a placeholder product: replace them with your own.
export const siteConfig = {
name: "Pulse",
slug: "pulse",
tagline: "Product analytics for teams that ship every week.",
description:
"Pulse turns every click, signup and upgrade into answers your whole team can act on.",
url: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
ogImage: "/opengraph-image",
links: {
twitter: "https://x.com/antonio_rnv",
github: "https://github.com/anthonyRanivoarison/app.launch.now",
},
supportEmail: process.env.NEXT_PUBLIC_SUPPORT_EMAIL,
creator: "@antonio_rnv",
locale: "en_US",
themeColor: "#09090b",
geo: {
region: "FR",
placename: "Paris, France",
latitude: "48.8566",
longitude: "2.3522",
},
}Good to know: siteConfig reads process.env directly instead of lib/env, so it can be imported from client components, tsx scripts and builds that run with SKIP_ENV_VALIDATION.
name
- Type:
string - Default:
"Pulse"
Display name. Read by the root metadata and title template (app/layout.tsx), app/manifest.ts, app/opengraph-image.tsx, the JSON-LD (lib/seo/json-ld.ts), createSeoHead (lib/seo/metadata.ts), the docs nav title (lib/layout.shared.ts), Better Auth's appName and the OTP email (lib/auth.ts), newsletter emails (lib/newsletter-actions.ts, features/newsletter/newsletter-composer.tsx), the password-set email (lib/account-actions.ts), legalConfig.product.name, scripts/seed-plans.ts, and user-facing copy in the auth pages, marketing sections, onboarding, changelog, status, error pages and the profile form.
slug
- Type:
string - Default:
"pulse"
Machine name. Used as:
- the API key prefix (
<slug>_, set asdefaultPrefixof theapiKeyplugin inlib/auth.ts), also shown as a fallback infeatures/account/api-keys/api-key-row.tsx; - the Stripe price lookup keys (
<slug>-pro-monthly,<slug>-ultra-monthly) and theappmetadata tag on Stripe products inscripts/stripe-create-products.tsandscripts/stripe-update-features.ts.
slug after you created Stripe products means pnpm stripe:products and pnpm stripe:update-features no longer find the existing prices by lookup key. Existing API keys keep their old prefix.tagline
- Type:
string
Short pitch. Read by the home page title (app/page.tsx), the Open Graph image and its alt (app/opengraph-image.tsx) and features/marketing/brand.tsx.
description
- Type:
string
Default meta description. Read by app/layout.tsx, app/page.tsx, app/manifest.ts, app/opengraph-image.tsx, lib/seo/json-ld.ts, and createSeoHead when a page passes no description.
url
- Type:
string - Default:
process.env.NEXT_PUBLIC_APP_URL, or"http://localhost:3000"when unset.
Public base URL. Used as metadataBase and to build absolute URLs in app/layout.tsx, lib/seo/metadata.ts, lib/seo/json-ld.ts, app/sitemap.ts, app/robots.ts, app/changelog/rss.xml/route.ts, and as legalConfig.product.url.
ogImage
- Type:
string - Default:
"/opengraph-image"
Path of the generated Open Graph image. No code currently reads this field; Next.js serves app/opengraph-image.tsx by file convention.
links
- Type:
{ twitter: string; github: string }
Social profiles. Rendered in the marketing footer (features/marketing/footer.tsx) and added to the Organization JSON-LD sameAs list (empty values are skipped).
supportEmail
- Type:
string | undefined - Default:
process.env.NEXT_PUBLIC_SUPPORT_EMAIL
When set, the app sidebar shows a Support mailto: link (components/layouts/nav-configs/dashboard.tsx). When unset, the link is hidden.
creator
- Type:
string - Default:
"@antonio_rnv"
Twitter / X handle used for twitter.creator in app/layout.tsx and createSeoHead.
locale
- Type:
string - Default:
"en_US"
Open Graph locale in app/layout.tsx and the default locale of createSeoHead.
themeColor
- Type:
string - Default:
"#09090b"
Browser theme color (viewport.themeColor in app/layout.tsx) and the manifest's background_color and theme_color.
geo
- Type:
{ region: string; placename: string; latitude: string; longitude: string }
Emitted by createSeoHead as geo.region, geo.placename, geo.position (latitude;longitude) and ICBM meta tags.
legalConfig
legalConfig holds everything the legal documents under /legal need. Any string that still starts with TODO is reported by pnpm legal:check (which exits with code 1) and shown in a warning banner on /legal pages in development. See Legal pages for the workflow.
effectiveDate
- Type:
string(ISO date,YYYY-MM-DD)
Date the current version takes effect. Formatted by formatEffectiveDate() in features/legal/types.ts and shown as the "last updated" date on /legal and each document.
product
| Field | Type | Default | Read by |
|---|---|---|---|
name | string | siteConfig.name | Every document, app/legal/page.tsx, the cookie banner. |
url | string | siteConfig.url | Not read by the current documents. |
description | string | TODO | Terms of Service. |
company
| Field | Type | Default | Notes |
|---|---|---|---|
legalName | string | TODO | Registered company name, or your full name if sole trader. |
legalForm | string | TODO | For example SAS, LLC, Ltd. companyName() renders legalName (legalForm) when set. |
registrationNumber | string | TODO | SIREN/RCS, Companies House number, EIN. |
registry | string | TODO | For example RCS Paris. Empty string if none. |
vatNumber | string | "" | Empty if not VAT registered. |
shareCapital | string | "" | Empty if not applicable. |
address | string | TODO | Street, postal code, city. |
country | string | TODO |
The Legal Notice lists all company fields and skips empty ones. companyName() is used by the Terms, Privacy Policy and DPA; address and country also appear in Terms and Privacy.
contact
| Field | Type | Default | Read by |
|---|---|---|---|
email | string | APP_CONTACT_EMAIL, else TODO | Terms, Legal Notice. |
privacyEmail | string | APP_CONTACT_EMAIL, else TODO | Privacy Policy, Cookie Policy, DPA. |
supportEmail | string | APP_HELP_EMAIL, else TODO | Refund Policy (billing and refund requests). |
dpoEmail | string | "" | Privacy Policy and DPA. When set, it replaces privacyEmail as the privacy contact. |
phone | string | "" | Legal Notice, when set. |
publicationDirector
- Type:
string - Default:
TODO
Person responsible for the content ("Directeur de la publication", required in France). Shown in the Legal Notice.
host
| Field | Type | Default |
|---|---|---|
name | string | TODO |
address | string | TODO |
url | string | TODO |
Listed in the Hosting section of the Legal Notice.
jurisdiction
| Field | Type | Default | Read by |
|---|---|---|---|
governingLaw | string | TODO | Terms. |
courts | string | TODO | Terms. |
supervisoryAuthority | string | TODO | Privacy Policy (GDPR art. 77 complaint authority). |
audience
| Field | Type | Default | Effect |
|---|---|---|---|
customers | "business-only" | "business-and-consumers" | "business-and-consumers" | With business-and-consumers, the Terms and Refund Policy include consumer-specific wording (withdrawal rights). |
minimumAge | number | 16 | Minimum age in the Terms and the Privacy Policy. |
applyGdpr | boolean | true | Includes the GDPR section in the Privacy Policy. |
applyCcpa | boolean | true | Includes the CCPA section in the Privacy Policy. |
billing
| Field | Type | Default | Effect |
|---|---|---|---|
paymentProvider | string | "Stripe" | Named in Terms, Privacy, Cookie and Refund policies. |
currency | string | "USD" | Terms. |
trialDays | number | 0 | When greater than 0, Terms and Refund Policy mention the free trial. |
refundWindowDays | number | 14 | Days during which a first payment can be refunded on request. 0 means no refunds. |
retention
| Field | Type | Default | Read by |
|---|---|---|---|
backupDays | number | 30 | Privacy Policy, DPA. |
logsDays | number | 90 | Privacy Policy. |
subprocessors
- Type:
Subprocessor[]
export type Subprocessor = {
name: string
purpose: string
/** Where the data is processed, e.g. "United States", "EU (Frankfurt)". */
location: string
url: string
}Listed in the Privacy Policy and the DPA. The default list covers Neon (database and object storage), Stripe, Resend, GitHub and Google. The Neon database location is a TODO.
cookies
- Type:
CookieEntry[]
export type CookieCategory =
"essential" | "preferences" | "analytics" | "marketing"
export type CookieEntry = {
name: string
provider: string
purpose: string
duration: string
category: CookieCategory
}Read by the Cookie Policy (which adds tracker wording when any cookie is analytics or marketing) and by the cookie consent dialog (features/cookie-consent/cookie-consent.tsx), which groups cookies by category and clears first-party cookies of a category the user declines. Defaults: better-auth.session_token, better-auth.last_used_login_method, better-auth.two_factor, cookie_consent, open_app (all essential) and sidebar_state (preferences).
Add an entry here whenever you add a cookie or a third-party script.
findLegalPlaceholders()
function findLegalPlaceholders(value?: unknown, path?: string): string[]Walks legalConfig and returns the dotted paths (for example company.address or subprocessors[0].location) of every string starting with TODO. Used by scripts/legal-check.ts and the /legal pages.
Environment variables
lib/env.ts validates environment variables with @t3-oss/env-nextjs and Zod. Import env from @/lib/env in server code instead of reading process.env:
import { env } from "@/lib/env"
const from = env.EMAIL_FROMSet SKIP_ENV_VALIDATION to any non-empty value to skip validation (for example in CI builds). Copy .env.example to .env to start.
Server variables
| Variable | Schema | Used by |
|---|---|---|
DATABASE_URL | URL, required | drizzle/db.ts, drizzle/migrate.ts, drizzle.config.ts, scripts/db-baseline.ts |
BETTER_AUTH_SECRET | string, min 32 chars, required | Better Auth; also signs newsletter confirmation tokens (features/newsletter/newsletter.server.ts). Generate with openssl rand -base64 32. |
BETTER_AUTH_URL | URL, required | Better Auth baseURL; links in invitation, notification and newsletter confirmation emails. |
RESEND_API_KEY | string, required | lib/email.ts |
RESEND_AUDIENCE_ID | string, optional | Newsletter. The feature is off when unset. |
EMAIL_FROM | string, required | Default sender, for example "Pulse <hello@example.com>". |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET | string, required | GitHub OAuth in lib/auth.ts. |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | string, required | Google OAuth in lib/auth.ts. |
AWS_ENDPOINT_URL_S3 | URL, required | S3-compatible storage endpoint (lib/storage.ts). |
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY | string, required | Storage credentials. |
AWS_REGION | string, required | Storage region (auto in .env.example). |
STRIPE_SECRET_KEY | string, required | lib/stripe.ts and the Stripe scripts. |
STRIPE_WEBHOOK_SECRET | string, required | Stripe webhook verification in lib/auth.ts. |
STRIPE_PRO_PRICE_ID, STRIPE_ULTRA_PRICE_ID | string, optional | Map the pro and ultra plans to Stripe prices. A plan without a price ID is skipped. Printed by pnpm stripe:products. |
DEMO_EMAIL | email, optional | Shared demo account. Destructive actions are refused for this user. |
DEMO_PASSWORD | string, min 8 chars, optional | Demo account password. The demo sign-in button shows only when both demo variables are set. |
APP_CONTACT_EMAIL | email, optional | legalConfig.contact.email and privacyEmail. |
APP_HELP_EMAIL | email, optional | legalConfig.contact.supportEmail; support address in the password-set email. |
APP_NOREPLY_EMAIL | email, optional | Sender for notification and account emails, falling back to EMAIL_FROM. |
APP_ONBOARDING_EMAIL | email, optional | Declared in the schema but not read by the current code. |
Client variables
| Variable | Schema | Used by |
|---|---|---|
NEXT_PUBLIC_APP_URL | URL, required | siteConfig.url, the Better Auth client baseURL (lib/auth-client.ts), trustedOrigins in lib/auth.ts, the newsletter confirm redirect. |
Not validated
| Variable | Used by |
|---|---|
NEXT_PUBLIC_SUPPORT_EMAIL | siteConfig.supportEmail. Optional, read directly from process.env. |
SKIP_ENV_VALIDATION | Disables validation in lib/env.ts. |
Add a variable
Declare it in the schema
Add it to server (or client, with the NEXT_PUBLIC_ prefix) and to runtimeEnv:
export const env = createEnv({
server: {
// ...
MY_API_KEY: z.string().min(1),
},
runtimeEnv: {
// ...
MY_API_KEY: process.env.MY_API_KEY,
},
})Document it
Add the variable to .env.example with a placeholder value.
Set it everywhere
Add it to your local .env and to your hosting provider's environment.