Configuration
Setup instructions and environment variables for Roxabi Boilerplate
Prerequisites
Installation
# Clone the repository
git clone <repo-url>
cd roxabi_boilerplate
# Install dependencies
bun installEnvironment Variables
Copy the example file and configure:
cp .env.example .envRequired Variables
| Variable | Description | Example |
|---|---|---|
NODE_ENV | Environment mode | development |
DATABASE_URL | PostgreSQL connection string | postgresql://roxabi:roxabi@localhost:5432/roxabi |
BETTER_AUTH_SECRET | Secret for BetterAuth | Random 32+ character string |
Branch Databases (Local Development)
Each git worktree gets its own isolated PostgreSQL database named roxabi_<issue_number> within the shared Docker container. This prevents schema conflicts when multiple worktrees run concurrent migrations.
Branch databases are created automatically by /implement during worktree setup, or manually:
cd apps/api
# Create a branch database (migrate + seed + update .env)
bun run db:branch:create
# Create with explicit issue number (non-interactive)
bun run db:branch:create --force 150
# Drop a branch database
bun run db:branch:drop
# List all branch databases and their worktree status
bun run db:branch:list
# Seed a fresh database with dev essentials
bun run db:seedSeed credentials: dev@roxabi.local / password123 (1 user, 1 organization, RBAC roles seeded).
All scripts run from apps/api/ and use docker exec against the container named by POSTGRES_CONTAINER (default: roxabi-postgres). No local psql installation is required.
Database Scripts (Root-Level)
Root-level wrappers load .env automatically and delegate to apps/api/. No need to cd into apps/api or set DATABASE_URL manually.
| Command | Description | Production |
|---|---|---|
bun db:up | Start PostgreSQL (Docker Compose) | — |
bun db:down | Stop PostgreSQL (preserves data) | — |
bun db:migrate | Apply pending Drizzle migrations | Allowed |
bun db:generate | Generate migrations from schema changes | Allowed |
bun db:seed | Seed dev data (user, org, RBAC) | Blocked |
bun db:reset | Truncate all tables (CASCADE) | Blocked |
Common workflow:
# Fresh start after cloning
bun db:up && bun db:migrate && bun db:seed
# Reset and re-seed during development
bun db:reset && bun db:seed
db:resetanddb:seedrefuse to run whenNODE_ENV=production(enforced at both the bash dispatcher and TypeScript script level).
Optional Variables
| Variable | Description | Default |
|---|---|---|
APP_URL | Frontend URL | http://localhost:3000 |
API_URL | Backend URL | http://localhost:4000 |
GITHUB_TOKEN | GitHub API access | - |
APP_NAME | Application name (emails, Swagger, page titles) | App |
Docker Overrides (local dev — multi-project)
Override these when running multiple projects on the same machine to avoid container name and volume conflicts:
| Variable | Description | Default |
|---|---|---|
POSTGRES_CONTAINER | Docker container name for Postgres | roxabi-postgres |
POSTGRES_PORT | Host port mapped to Postgres container | 5432 |
MAILPIT_CONTAINER | Docker container name for Mailpit | roxabi-mailpit |
POSTGRES_VOLUME | Named Docker volume for Postgres data | postgres_data |
DB_BRANCH_PREFIX | Prefix for per-worktree branch databases | POSTGRES_DB value (roxabi) |
Set these in .env before running bun db:up. The branch database scripts (db:branch:*) pick up POSTGRES_CONTAINER and DB_BRANCH_PREFIX automatically.
Docker Overrides (local dev — multi-project)
Override these when running multiple projects on the same machine to avoid container name and volume conflicts:
| Variable | Description | Default |
|---|---|---|
POSTGRES_CONTAINER | Docker container name for Postgres | roxabi-postgres |
POSTGRES_PORT | Host port mapped to Postgres container | 5432 |
MAILPIT_CONTAINER | Docker container name for Mailpit | roxabi-mailpit |
POSTGRES_VOLUME | Named Docker volume for Postgres data | postgres_data |
DB_BRANCH_PREFIX | Prefix for per-worktree branch databases | POSTGRES_DB value (roxabi) |
Set these in .env before running bun db:up. The branch database scripts (db:branch:*) pick up POSTGRES_CONTAINER and DB_BRANCH_PREFIX automatically.
Public Variables (exposed to client)
| Variable | Description | Default |
|---|---|---|
VITE_APP_NAME | Application name shown in UI (logo, titles, footer) | App |
VITE_GITHUB_REPO_URL | GitHub repository URL shown in UI | - |
Claude Code Agent Teams
| Variable | Description | Default |
|---|---|---|
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS | Enable multi-agent coordination (experimental) | 0 |
CLAUDE_CODE_TEAMMATE_MODE | Controls teammate display: in-process (same terminal) or split-pane (separate panes) | in-process |
To enable agent teams, set in your shell:
export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1Agent definitions are in .claude/agents/*.md. See the Agent Teams Guide for details.
Complete Environment Variable Reference
Environment files (
.env,.env.local) are resolved from the monorepo root only. Do not place.envfiles in app directories.
Core
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
NODE_ENV | API, Web | No | development | Server | Environment mode |
API_PORT | API | No | 4000 | Server | API server port. On Vercel, the platform-injected PORT takes precedence. |
APP_PORT | Web | No | 3000 | Server | Web dev server port (Vite) |
EMAIL_PORT | No | 3001 | Server | Email preview server port | |
API_URL | Web | Dev: No, Prod: Yes | http://localhost:4000 | Server | Backend API URL |
APP_URL | API, Web | No | — | Server | Frontend URL |
CORS_ORIGIN | API | No | http://localhost:3000 | Server | Allowed CORS origin |
LOG_LEVEL | API | No | debug | Server | Log verbosity |
APP_NAME | API, Web, Docs | No | App | Server | Application name (emails, Swagger, fixtures) |
Database
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
DATABASE_URL | API | Yes (prod) | — | Server | PostgreSQL connection string |
DATABASE_APP_URL | API | No | — | Server | App user connection URL (RLS enforced — use this instead of DATABASE_URL for the API server) |
Authentication
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
BETTER_AUTH_SECRET | API | Yes (non-dev) | dev-secret-... | Server | Auth session secret |
BETTER_AUTH_URL | API | No | Derived from APP_URL | Server | Auth base URL (auto-derived — see Derived Variables) |
GOOGLE_CLIENT_ID | API | No | — | Server | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | API | No | — | Server | Google OAuth client secret |
GITHUB_CLIENT_ID | API | No | — | Server | GitHub OAuth client ID |
GITHUB_CLIENT_SECRET | API | No | — | Server | GitHub OAuth client secret |
RESEND_API_KEY | API | No | — | Server | Resend email API key |
EMAIL_FROM | API | No | noreply@yourdomain.com | Server | Sender email |
Rate Limiting & API Features
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
KV_REST_API_URL | API | Prod (if rate limit) | — | Server | Upstash Redis URL |
KV_REST_API_TOKEN | API | Prod (if rate limit) | — | Server | Upstash Redis token |
RATE_LIMIT_ENABLED | API | No | true | Server | Rate limiting toggle |
SWAGGER_ENABLED | API | No | — | Server | Swagger UI toggle |
RATE_LIMIT_GLOBAL_TTL | API | No | 60000 | Server | Global window (ms) |
RATE_LIMIT_GLOBAL_LIMIT | API | No | 60 | Server | Global max requests |
RATE_LIMIT_AUTH_TTL | API | No | 60000 | Server | Auth window (ms) |
RATE_LIMIT_AUTH_LIMIT | API | No | 5 | Server | Auth max requests |
RATE_LIMIT_AUTH_BLOCK_DURATION | API | No | 300000 | Server | Auth block (ms) |
RATE_LIMIT_API_TTL | API | No | 60000 | Server | API key window (ms) |
RATE_LIMIT_API_LIMIT | API | No | 100 | Server | API key max requests |
Scheduled Tasks
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
CRON_SECRET | API | No | — | Server | Shared secret for authenticating cron job requests |
Deployment
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
VERCEL_ENV | API, Web | No | — | Server | Auto-injected by Vercel (production / preview / development) |
Public (Client-Side)
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
VITE_APP_NAME | Web | No | App | Client | Application name (page titles, logo, footer) |
VITE_GITHUB_REPO_URL | Web | No | — | Client | GitHub URL in header |
Docker (local dev)
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
POSTGRES_CONTAINER | — | No | roxabi-postgres | Local | Postgres Docker container name |
POSTGRES_PORT | — | No | 5432 | Local | Host port mapped to Postgres container |
MAILPIT_CONTAINER | — | No | roxabi-mailpit | Local | Mailpit Docker container name |
POSTGRES_VOLUME | — | No | postgres_data | Local | Named Docker volume for Postgres data |
DB_BRANCH_PREFIX | — | No | POSTGRES_DB | Local | Prefix for worktree branch databases |
Tooling
| Variable | App | Required | Default | Scope | Description |
|---|---|---|---|---|---|
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS | — | No | 0 | Local | Agent teams toggle |
GITHUB_TOKEN | — | No | — | Local/CI | GitHub API access |
Derived Variables
Some environment variables default to the value of another variable when not explicitly set. This allows common deployments to be configured with a single value while still supporting advanced overrides.
| Variable | Defaults to | When to override |
|---|---|---|
CORS_ORIGIN | APP_URL | Multi-origin setups — e.g., staging with branch aliases that differ from the primary APP_URL |
BETTER_AUTH_URL | APP_URL | Split-domain deployments where the auth service runs on a different origin from the frontend |
VITE_APP_NAME | APP_NAME | When the browser-visible name should differ from the server-side application name |
Precedence: an explicit value in .env always takes precedence silently — the fallback only applies when the variable is absent or empty.
For typical single-domain deployments, setting APP_URL and APP_NAME is sufficient and the derived variables require no further configuration.
Rate Limit Presets
RATE_LIMIT_PRESET selects a named configuration that sets sensible rate limit defaults as a group. This avoids having to tune five individual variables for common scenarios.
| Preset | Global Limit | Auth Limit | Auth Block (ms) | Notes |
|---|---|---|---|---|
default | 60 req/min | 5 req/min | 300,000 | Balanced (production default) |
strict | 30 req/min | 3 req/min | 600,000 | Enhanced auth protection |
relaxed | 120 req/min | 10 req/min | 60,000 | Permissive (dev/testing) |
On/off toggle: RATE_LIMIT_ENABLED remains the independent kill switch — it disables rate limiting entirely regardless of which preset is active.
Individual overrides: any of the RATE_LIMIT_* variables set explicitly in .env take precedence over the preset values for that specific parameter. This allows mixing presets with targeted adjustments without having to redeclare every value.
Dev-core Configuration
Agent orchestration settings are stored in .claude/dev-core.yml, which is committed to the repository. This file holds GitHub Projects field IDs and other workflow metadata used by Claude Code agent orchestration — it has no effect on the application at runtime.
The file is auto-populated by the /init skill when you first set up the project. You do not normally need to edit it by hand.
Values are resolved using a 3-tier fallback:
- YAML —
.claude/dev-core.yml(committed, shared across the team) - Environment variable — shell or
.envoverride for per-developer customization - Live lookup —
gh repo viewforGITHUB_REPOwhen neither of the above is present
TypeScript Configuration
Root tsconfig.json provides base settings. Each app/package extends it:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist"
}
}Biome Configuration
Linting and formatting configured in biome.json:
- 2-space indentation
- Single quotes
- No semicolons (ASI)
- 100 character line width
TurboRepo Configuration
Tasks defined in turbo.jsonc:
dev- Run development serversbuild- Build all packagescodegen- Generate routes and i18n filestypecheck- Type checkinglint- Run lintertest- Run testsclean- Remove build artifacts and cacheslicense:check- Scan dependency licenses for compliance
Environment Variables in Turbo
The root turbo.jsonc declares env arrays for variables that affect build output — changing these values invalidates the Turbo cache:
codegen:API_URL,APP_URL,VITE_*build:API_URL,APP_URL,KV_*,REDIS_*,VITE_*test:CI,VITEST
Wildcard patterns (e.g. VITE_*) automatically match all env vars with that prefix — no manual update to turbo.jsonc needed when adding new vars with a known prefix. For new prefixes, add a wildcard pattern to the relevant task.
The API app extends the root config via apps/api/turbo.json with passThroughEnv for runtime secrets (database, auth). These are available at runtime but don't affect the build cache, preventing unnecessary rebuilds when only secrets change.
IDE Setup
VS Code
Recommended extensions:
- Biome
- TypeScript
- Turbo Console Log
Settings:
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true
}Production Environment
Production runs entirely on Vercel with two projects (web + API). Environment variables are configured in each project's dashboard.
Vercel — Web Project
Configure in Vercel dashboard under Settings > Environment Variables:
| Variable | Description | Example |
|---|---|---|
API_URL | Vercel API project URL | https://api.roxabi.vercel.app |
APP_URL | This project's URL | https://app.roxabi.vercel.app |
Vercel — API Project
Configure in Vercel dashboard under Settings > Environment Variables (manual vars) or via Marketplace Integrations (auto-injected vars):
| Variable | Description | Source | Example |
|---|---|---|---|
DATABASE_URL | PostgreSQL connection (Neon) | Auto-injected by Neon Marketplace integration | postgresql://user:pass@host/neondb?sslmode=require |
DATABASE_APP_URL | App user connection URL (RLS enforced) | Manual | postgresql://app_user:pass@host/neondb?sslmode=require |
BETTER_AUTH_SECRET | Auth secret (random 32+ chars) | Manual | <random-string> |
APP_URL | Web project URL | Manual | https://app.roxabi.vercel.app |
API_URL | This project's URL | Manual | https://api.roxabi.vercel.app |
EMAIL_FROM | Sender email address | Manual | noreply@example.com |
RESEND_API_KEY | Resend API key for transactional emails | Auto-injected by Resend Marketplace integration | re_... |
KV_REST_API_URL | Upstash Redis REST URL | Auto-injected by Upstash Marketplace integration | https://...upstash.io |
KV_REST_API_TOKEN | Upstash Redis REST token | Auto-injected by Upstash Marketplace integration | AX... |
RATE_LIMIT_ENABLED | Enable/disable rate limiting (kill switch) | Manual | true |
RATE_LIMIT_PRESET | Rate limit preset (default / strict / relaxed) — see Rate Limit Presets | Manual | default |
SWAGGER_ENABLED | Enable Swagger UI at /api/docs | Manual | false |
Auto-injected vars are managed by their respective Vercel Marketplace integrations and do not need manual configuration. They appear with an integration badge in the Vercel dashboard. For local development, these vars still need to be set manually in
.env.
Note: Vercel auto-sets
NODE_ENV=production. NestJS runs as a Vercel Function with Fluid compute.
See the Deployment Guide for full setup instructions.