Orbit
01 · Getting started

Environment variables

Everything in apps/api/.env.example, grouped by what it turns on. The web shells need only two.

The API's env file is the source of truth for almost every adapter choice. Start by copying the example:

cp apps/api/.env.example apps/api/.env

Then fill in what you need. Only two variables are strictly required to boot the API:

VarRequiredDefault
DATABASE_URLyes
BETTER_AUTH_SECRETyeschange-me-in-prod
Heads up

BETTER_AUTH_SECRET signs every session token. In prod, generate a long random string — openssl rand -hex 32 is fine. If you leak or rotate it, every existing session logs out.

Database

VarWhat it does
DATABASE_URLPostgres connection string. Used at runtime by the API and by migrations (Prisma or drizzle-kit).
DIRECT_DATABASE_URLOptional non-pooled URL. Set this if DATABASE_URL points at a transaction pooler (PgBouncer, PlanetScale psdb) — migration tools need direct connections for advisory locks.

Auth & origins

VarDefaultWhat it does
BETTER_AUTH_SECRETchange-me-in-prodSession signing key. Required in every environment.
MAGIC_LINK_TTL_MIN15How long a magic link stays valid after it's minted.
API_ORIGINhttp://localhost:4002Where the API answers. Used in mail templates and OAuth redirects.
WEB_ORIGINhttp://localhost:4001Primary authenticated-app origin (allowed by CORS, trusted by better-auth).
WWW_ORIGINhttp://localhost:4000Marketing site origin.
ADDITIONAL_WEB_ORIGINShttp://localhost:4003Comma-separated list of extra allowed web origins. Handy when running both web-tanstack and web-next against the same API.
PORT4002Port the API listens on. Change this if you change API_ORIGIN.

OAuth providers (optional)

better-auth only registers providers whose client id and secret are both set. Leave them unset to disable:

  • GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET
  • APPLE_CLIENT_ID + APPLE_CLIENT_SECRET

Redirect URIs to register with each provider: ${API_ORIGIN}/v1/auth/callback/google and ${API_ORIGIN}/v1/auth/callback/apple.

Email

VarWhat it does
RESEND_API_KEYUnset = ConsoleMailer (logs links to stdout + exposes them at GET /v1/dev/last-magic-link). Set = Resend in prod.
RESEND_FROMSender name + address. Format: Orbit <hello@yourdomain.com>
RESEND_SEND_IN_DEVSet to 1 to force Resend even in dev — useful for template QA.

Billing

BILLING_PROVIDER is the master switch. When unset or set to noop, the billing routes return a disabled-state response and no provider SDK is constructed. Set it to one of stripe / polar / dodo to wire the matching adapter.

Plan catalog

BILLING_PLANS_JSON is a JSON array of plan descriptors rendered on the billing settings page. Keep it small — once you outgrow a handful of plans, replace the env-backed source in composition.ts with a DB-backed provider.

BILLING_PLANS_JSON='[
{
"key": "pro",
"name": "Pro",
"priceId": "price_...",
"unitAmount": 800,
"currency": "usd",
"interval": "month",
"intervalCount": 1,
"features": ["Unlimited teams", "Priority email"]
}
]'

Per-provider secrets

ProviderVars
StripeSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
PolarPOLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, POLAR_SERVER (sandbox | production)
DodoDODO_PAYMENTS_API_KEY, DODO_PAYMENTS_WEBHOOK_KEY, DODO_PAYMENTS_ENVIRONMENT (test_mode | live_mode)

Background jobs

JOBS_PROVIDER picks the adapter for the JobQueue and JobRuntime ports. Leave unset or set to noop to boot without background work; services that would enqueue a job 409 instead.

VarScopeWhat it does
JOBS_PROVIDERbothgraphile (Postgres-backed, self-hosted) / qstash (Upstash managed HTTP) / noop.
WORKER_DATABASE_URLgraphileOptional direct/session URL for LISTEN/NOTIFY. Fallback is DATABASE_URL.
JOBS_CONCURRENCYgraphileParallelism for this worker instance. Default 2.
QSTASH_TOKENqstashPublish-side API token from the Upstash console.
QSTASH_CURRENT_SIGNING_KEYqstashCurrent webhook-verification key (rotated by QStash).
QSTASH_NEXT_SIGNING_KEYqstashNext webhook-verification key; both are checked on delivery.
QSTASH_CALLBACK_URLqstashPublic base URL of this API. Must be internet-reachable in dev (smee, ngrok, tunnel).

Rate limiting

RATE_LIMIT_PROVIDER picks the adapter for the RateLimiter port. Leave unset or set to memory for the in-process sliding-window bucket; set to upstash for a Redis-backed limiter shared across workers and serverless instances.

VarScopeWhat it does
RATE_LIMIT_PROVIDERallnoop / memory (default) / upstash / unkey.
UPSTASH_REDIS_REST_URLupstashREST URL for the Upstash Redis database.
UPSTASH_REDIS_REST_TOKENupstashREST token with read/write access.
UNKEY_ROOT_KEYunkeyRoot API key from the Unkey dashboard.
UNKEY_RATELIMIT_NAMESPACEunkeyNamespace prefix. Defaults to orbit.
Heads up

The memory adapter is single-process only — state is not shared across workers, is lost on restart, and does NOT protect a horizontally scaled or serverless deploy. The API warns at boot if it's used in production. See /docs/integrations/rate-limiting for the full rundown.

Uploads (optional)

UPLOADTHING_TOKEN enables the signed-upload endpoints. Leave unset to disable uploads entirely — the UI hides the avatar upload affordance when it sees the disabled state.

Waitlist mode (optional)

  • IS_WAITLIST_ENABLEDtrue replaces the final step of /onboarding with a "you're on the list" screen instead of creating a workspace. POST /v1/waitlist accepts submissions regardless.
  • WAITLIST_ADMIN_SECRET — If set, POST /v1/waitlist/accept with Authorization: Bearer <secret> marks an entry approved.

Webhook tunneling (dev)

VarWhat it does
SMEE_URLYour personal smee.io URL. Register it as your provider's webhook endpoint; the apps/webhook-tunnel workspace reads this and forwards POSTs to the local API.
SMEE_TARGET_PATHRoute to forward to. Defaults to /v1/billing/webhooks/stripe — switch to /polar or /dodo for the other adapters.

Seed overrides (optional)

The seed script reads a handful of env vars so you can point it at your own email and workspace slug in dev:

  • SEED_OWNER_EMAIL — default owner@wereorbit.com
  • SEED_OWNER_NAME — default Demo Owner
  • SEED_WORKSPACE_SLUG — default demo
  • SEED_WORKSPACE_NAME — default Demo

The web shells

The TanStack and Next shells need only two build-time vars, baked into the bundle by Vite / Next at build time:

VarWhat it does
VITE_API_URLBase URL the client hits for REST and WebSocket traffic.
VITE_WEB_URLCanonical origin for the authenticated app — used in email links and share URLs.

The marketing site (apps/www) needs only VITE_WEB_URL, for the sign-in and "get access" buttons.