Getting started
Configuration
All configuration lives in two places: .env (secrets and service keys) and opsdash.config.ts (feature overrides and branding).
Environment variables
Required
| Variable | Description | Source |
|---|---|---|
| NEXT_PUBLIC_SUPABASE_URL | Supabase project URL | supabase start or Dashboard → Settings → API |
| NEXT_PUBLIC_SUPABASE_ANON_KEY | Public anon key | Same |
| SUPABASE_SERVICE_ROLE_KEY | Service role key — never expose to client | Same — click Reveal |
| NEXT_PUBLIC_APP_URL | App full URL, no trailing slash | http://localhost:3000 for dev |
Stripe (Billing)
| Variable | Description |
|---|---|
| STRIPE_SECRET_KEY | Stripe secret key (sk_test_... or sk_live_...) |
| NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key (pk_test_... or pk_live_...) |
| STRIPE_WEBHOOK_SECRET | Webhook signing secret (whsec_...) |
| STRIPE_PRICE_PRO_MONTHLY | Price ID for Pro plan, monthly |
| STRIPE_PRICE_PRO_YEARLY | Price ID for Pro plan, yearly |
| STRIPE_PRICE_ENTERPRISE_MONTHLY | Price ID for Enterprise plan, monthly |
| STRIPE_PRICE_ENTERPRISE_YEARLY | Price ID for Enterprise plan, yearly |
Email (Resend)
| Variable | Description |
|---|---|
| RESEND_API_KEY | Resend API key — get at resend.com |
| RESEND_FROM | Optional sender address — e.g. OpsDash <noreply@yourdomain.com> |
If RESEND_API_KEY is not set, invitation emails are skipped silently. Invite links still work — share them manually.
Other variables
| Variable | Purpose |
|---|---|
| CRON_SECRET | Protects cron endpoints — openssl rand -hex 32 |
| PLATFORM_ADMIN_EMAILS | Comma-separated emails with platform admin access (case-sensitive) |
| VAPID_PUBLIC_KEY | VAPID public key for web push — npx web-push generate-vapid-keys |
| VAPID_PRIVATE_KEY | VAPID private key (server-side only) |
| NEXT_PUBLIC_VAPID_PUBLIC_KEY | Same as VAPID_PUBLIC_KEY (needed client-side) |
| GOOGLE_GMAIL_CLIENT_ID | Google OAuth client ID for Gmail sync |
| GOOGLE_GMAIL_CLIENT_SECRET | Google OAuth client secret |
| OPENAI_API_KEY | OpenAI API key (optional — AI features) |
Stripe setup
1 — Create products and prices
In Stripe Dashboard → Products → Add product:
- Pro → Monthly
$29/month→ copy Price ID →STRIPE_PRICE_PRO_MONTHLY. Yearly$290/year→STRIPE_PRICE_PRO_YEARLY. - Enterprise → Monthly
$99/month→STRIPE_PRICE_ENTERPRISE_MONTHLY. Yearly$990/year→STRIPE_PRICE_ENTERPRISE_YEARLY.
2 — Configure webhook
Stripe Dashboard → Developers → Webhooks → Add endpoint:
- URL:
https://yourdomain.com/api/webhooks/stripe - Events:
checkout.session.completed,invoice.payment_succeeded,customer.subscription.updated,customer.subscription.deleted - Copy the signing secret →
STRIPE_WEBHOOK_SECRET
For local testing:
stripe listen --forward-to localhost:3000/api/webhooks/stripeTest card: 4242 4242 4242 4242, any future date, any CVC.
opsdash.config.ts
For private instance deployments, this is the only file a buyer needs to edit. It overrides modules, features, branding, and billing without touching application code.
import { defineConfig } from '@opsdash/config';
export default defineConfig({
branding: {
name: 'ClientOS',
logo: '/logos/client.png',
primaryColor: '#1a56db',
},
modules: {
crm: { enabled: true },
projects: { enabled: true },
automation: { enabled: false }, // disable entire module
billing: { enabled: true },
},
features: {
'crm:contacts': {
usageLimit: { free: 500, pro: 25_000, enterprise: 'unlimited' },
},
},
auth: {
providers: ['email', 'google'],
},
nav: {
order: ['dashboard', 'crm', 'projects', 'forms', 'settings'],
},
});Branding
- Set
branding.nameinopsdash.config.ts - Replace
public/logo.svgandpublic/logo-icon.svg - Set
branding.accentColoras HSL values (e.g.221 83% 53%)
Full guide: Whitelabel configuration