vibesboarddocs

Environment variables#

Start from .env.example in the repository root — copy it to .env (or .env.local) and fill in the placeholders. Generate secrets with openssl rand -hex 32.

The Next.js app reads apps/web/.env.local, because next dev and the production server run with apps/web as their working directory. The root .env / .env.local files reach root-level scripts (db:migrate, db:seed, scripts/*) and the E2E harness. In most setups you keep both in sync.

Tables below list Required as it's presented in .env.example: variables that ship uncommented in the template are needed for a normal deployment; variables commented out with # are optional and the app runs without them.

Platform AI#

These configure the platform-wide LLM fallback — used only when a workspace has no matching provider configured under Settings → LLM Providers. Tenant provider keys are never stored in the environment; they live encrypted in the database. See bring your own LLM.

NameRequiredDescription
OPENAI_API_KEYYesPlatform fallback API key
OPENAI_MODELYesDefault chat model for the platform fallback
OPENAI_BASE_URLNoOptional OpenAI-compatible gateway/proxy endpoint (also used by the local E2E mock). Third-party gateways that only implement /chat/completions and /embeddings cannot back this fallback, since @ai-sdk/openai@4 calls /responses — configure those providers per workspace instead
OPENAI_VISION_MODELNoOverride the vision-capable model (defaults to gpt-5.4-nano)
OPENAI_AGENT_CREATOR_MODELNoOverride the model used by the agent-creation assistant (defaults to gpt-5.4-nano)
OPENAI_EMBEDDINGS_MODELNoOverride the embeddings model (defaults to text-embedding-3-small). Stored vectors must be 384, 768, 1024, or 1536 dimensions

Application and auth#

Core application URL, session signing, and access-control secrets.

NameRequiredDescription
NEXT_PUBLIC_APP_URLYesCanonical app URL and Better Auth callback base — a mismatch with the port the app is served on breaks sign-in
BETTER_AUTH_SECRETYesServer-side session signing secret
AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRETNoGoogle sign-in is enabled only when both are set; leave unset to disable it
ACCESS_GATE_SECRETYesHashes access passwords and signs access cookies for password-gated public agents
ENCRYPTION_KEYYesCurrent AES-256-GCM master key for tenant LLM, OAuth, and channel credentials; use a strong environment-specific value
ENCRYPTION_KEYS_OLDNoComma-separated retired master keys kept temporarily for decrypt-only access during rotation; remove each after affected values have been re-sealed
CRON_SECRETYesAuthenticates scheduled and background endpoints
RATE_LIMIT_SALTYesHMAC salt for anonymous-request rate-limit identifiers; use a different value per environment. ENCRYPTION_KEY is a fallback, but a dedicated salt keeps the two purposes independent
MONTHLY_MESSAGE_LIMITNoOptional monthly workspace message cap; omit for unlimited self-hosted usage
PUBLIC_CHAT_RATE_LIMIT_WINDOW_MS, PUBLIC_CHAT_SESSION_RATE_LIMIT, PUBLIC_CHAT_ADDRESS_RATE_LIMIT, PUBLIC_CHAT_AGENT_RATE_LIMITNoFixed-window rate limits for the public chat endpoint (per session, per address, per agent), stored in Postgres
PUBLIC_REVIEW_RATE_LIMIT_WINDOW_MS, PUBLIC_REVIEW_SESSION_RATE_LIMIT, PUBLIC_REVIEW_ADDRESS_RATE_LIMIT, PUBLIC_REVIEW_AGENT_RATE_LIMITNoSame, for the public review-generation endpoint
SMOKE_TEST_SECRETNoEnables the model-backed /api/smoke route, which is otherwise disabled. Configure only in CI or a private diagnostic environment — never as a health check

NEXT_PUBLIC_AUTH_GOOGLE also exists in .env.example, but it is a build-time-only value read by CI (ci-build.yml) and deploy-cloudrun.yml as a build arg — application code never reads it. Google sign-in is controlled entirely by whether AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET are set.

Data and storage#

Postgres connection roles and S3-compatible object storage.

NameRequiredDescription
DATABASE_URLYesApp role connection string, subject to row-level security. Used by the Next.js app and workers
DATABASE_MIGRATE_URLYesMigrate role connection string, BYPASSRLS. Used by drizzle-kit and the seed script, and at request time by admin-panel API routes (via getMigrateDb(), gated behind requireSuperAdmin()) — not only an offline migration credential
DATABASE_POOL_MAXNoOptional application connection-pool size (defaults to 10)
S3_ENDPOINTYesS3-compatible storage endpoint (MinIO in dev; your S3/R2/B2/etc. endpoint in production)
S3_REGIONYesStorage region
S3_BUCKETYesStorage bucket name
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYYesStorage credentials
S3_FORCE_PATH_STYLEYestrue for MinIO; false for AWS S3 / Cloudflare R2 / etc. (virtual-hosted style)

Integrations#

Webhook, messaging-channel, and third-party OAuth configuration. WhatsApp and Instagram accounts are configured per workspace and stored encrypted in the database — only the webhook verification secrets below belong in the environment.

NameRequiredDescription
WHATSAPP_INBOX_VERIFY_TOKENYesWebhook verification token for the WhatsApp inbox. See WhatsApp
INSTAGRAM_INBOX_VERIFY_TOKENYesWebhook verification token for the Instagram inbox. See Instagram
META_APP_SECRETYesMeta app secret, used for webhook signature verification
NEXT_PUBLIC_META_APP_IDNoMeta app identifier used by the inbox onboarding UI
NEXT_PUBLIC_FB_LOGIN_CONFIG_IDNoFacebook Login config ID used by the inbox onboarding UI
GOOGLE_CALENDAR_CLIENT_ID, GOOGLE_CALENDAR_CLIENT_SECRETYesGoogle Calendar OAuth client. See Google Calendar
GOOGLE_SHEETS_CLIENT_ID, GOOGLE_SHEETS_CLIENT_SECRETNoDedicated Google Sheets OAuth client; falls back to the Calendar client credentials when unset. See Google Sheets
GOOGLE_SERVICE_ACCOUNT_KEYYesService-account JSON, read only by scripts/register-risc.ts to register Google Cross-Account Protection (RISC)
RESEND_API_KEYNoEnables email delivery for verification, password reset, magic links, and notifications. Without it, these URLs are printed to the server console instead
NOTIFICATION_EMAIL_FROMNoSender identity for application email, e.g. Vibesboard <notifications@yourdomain.com>

OAuth redirect URIs for Google Calendar and Google Sheets are derived from NEXT_PUBLIC_APP_URL — do not set them separately.

Rendered on the /privacy-policy and /terms-of-service pages. Nothing is hardcoded, since this repository is public: a baked-in default would be inherited by every fork, and an unconfigured deployment would name someone else's company as the data controller. Leave all of these unset for local development. Before accepting real users, set the values that identify the controller and a privacy contact: GDPR Article 13 requires identifying the controller, and both Google OAuth verification and Meta app review check for it. Follow the privacy and data-rights operations runbook to establish retention and request-handling processes.

NameRequiredDescription
LEGAL_ENTITY_NAMENoOperating entity name, e.g. Example AB
LEGAL_ENTITY_REGISTRATION_NUMBERNoCompany registration number
LEGAL_ENTITY_VAT_NUMBERNoVAT number
LEGAL_ENTITY_ADDRESSNoRegistered address; use | to separate rendered lines, e.g. Example Street 1|111 22 Stockholm|Sweden
LEGAL_GOVERNING_COUNTRYNoGoverning law country
LEGAL_FORUMNoLegal forum, e.g. the Stockholm District Court
LEGAL_SUPERVISORY_AUTHORITYNoData-protection supervisory authority name
LEGAL_SUPERVISORY_AUTHORITY_URLNoSupervisory authority URL
LEGAL_CONTACT_EMAILNoPrivacy contact address
LEGAL_SERVICE_HOSTNoService hostname referenced in the legal documents

Deployment scripts (not read by the app)#

NameRequiredDescription
GCP_PROJECT_IDNoRead only by scripts/setup-secrets.sh, the GCP secret provisioning helper — not by the application itself

Next steps#