CharleOS uses environment variables for configuration. The .env.local file is included in the repository and pre-configured for local development.
The .env.local file is already set up for local development. You typically don’t need to modify these values unless you’re setting up integrations or testing specific features.
Core Configuration
Database
Variable Value Description DATABASE_URLPostgreSQL connection string Connection to Neon database (DEV branch for local)
Local development:
Uses the DEV branch of the Neon database
Safe to experiment without affecting production data
Automatically configured in .env.local
Authentication
Variable Value Description BETTER_AUTH_SECRETRandom 32+ char string Secret for encrypting sessions BETTER_AUTH_URLhttp://localhost:3000Base URL for auth server NEXT_PUBLIC_BETTER_AUTH_URLhttp://localhost:3000Public-facing auth URL (client-side) NEXT_PUBLIC_APP_URLhttp://localhost:3000Main app URL NEXT_PUBLIC_CLIENT_PORTAL_URLhttp://clients.localhost:3000Client portal URL
Local vs Production:
Auth URLs must be http://localhost:3000 for local development.
Production uses https://charle.agency.
Never use production URLs locally or you’ll get CORS errors.
Google OAuth
Variable Description GOOGLE_CLIENT_IDOAuth client ID from Google Cloud Console GOOGLE_CLIENT_SECRETOAuth client secret
How to get these:
Go to Google Cloud Console
Select the CharleOS project
Find the OAuth 2.0 Client ID credentials
Only @charle.co.uk email addresses are allowed to sign in.
Integrations
Cloudflare R2 (File Storage)
Variable Description CLOUDFLARE_WORKER_URLURL of the R2 API worker (https://r2-api.charle.agency) R2_UPLOAD_SECRETShared secret for worker authentication CLOUDFLARE_R2_PUBLIC_URLPublic URL for accessing uploaded files (https://files.charle.agency)
What it’s for:
Stores file attachments (task attachments, ticket files, etc.)
Accessed via a Cloudflare Worker for security
Files are served from files.charle.agency
Architecture:
CharleOS → Worker (r2-api.charle.agency) → R2 Bucket → Public URL (files.charle.agency)
Pusher (Real-time & Notifications)
CharleOS uses Pusher for two purposes:
Pusher Channels (Real-time updates)
Variable Description NEXT_PUBLIC_PUSHER_KEYPublic API key (client-side) NEXT_PUBLIC_PUSHER_CLUSTERServer cluster (eu) PUSHER_APP_IDApplication ID (server-side) PUSHER_SECRETSecret key (server-side)
What it’s for:
Real-time updates to task status
Live notifications in the UI
Multi-user collaboration features
Pusher Beams (Push notifications)
Variable Description NEXT_PUBLIC_PUSHER_BEAMS_INSTANCE_IDBeams instance identifier PUSHER_BEAMS_SECRET_KEYSecret for sending push notifications
What it’s for:
Browser push notifications
Desktop notifications for tasks and mentions
Resend (Email)
Variable Description RESEND_API_KEYAPI key for sending emails
What it’s for:
Sending email notifications
Quote approval emails to clients
Task assignment notifications
System alerts
Get an API key:
Algolia (Search)
Variable Description ALGOLIA_APP_IDAlgolia application ID ALGOLIA_ADMIN_API_KEYAdmin API key (for indexing) ALGOLIA_SEARCH_API_KEYSearch-only API key (public, read-only) ALGOLIA_INDEX_NAMEIndex name (search_entities_dev for local)
What it’s for:
Global search across clients, tasks, quotes, projects
Instant search results as you type
Indexed data includes: clients, tasks, quotes, projects, tickets
Index names:
Local/Dev: search_entities_dev
Production: search_entities_prod
HubSpot (CRM Integration)
Variable Description HUBSPOT_ACCESS_TOKENPrivate app access token
What it’s for:
Syncing client data with HubSpot CRM
Creating deals and contacts
Tracking client interactions
Optional: Only needed if using HubSpot integration features.
OpenAI (AI Features)
Variable Description OPENAI_API_KEYOpenAI API key for AI features
What it’s for:
Alan (AI assistant) for scheduling and task management
Smart suggestions for task sizing
Natural language processing for search
Slack (Notifications)
Variable Description SLACK_BOT_TOKENBot user OAuth token (xoxb-...) SLACK_SIGNING_SECRETSigning secret for verifying requests SLACK_APP_TOKENApp-level token (xapp-...) SLACK_APP_IDSlack app identifier
What it’s for:
Sending notifications to Slack channels
Task updates in Slack
Quote approvals and mentions
Integration with team communication
Setup:
Go to Slack API Dashboard
Select the CharleOS app
Find credentials in “OAuth & Permissions” and “Basic Information”
Sentry (Error Tracking)
Variable Description SENTRY_AUTH_TOKENAuth token for uploading source maps
What it’s for:
Error tracking and monitoring
Performance monitoring
Release tracking with source maps
Get a token:
Go to Sentry Settings
Create a token with project:releases and org:read scopes
Environment-Specific Values
Local Development (.env.local)
Variable Local Value DATABASE_URLDEV database branch BETTER_AUTH_URLhttp://localhost:3000NEXT_PUBLIC_BETTER_AUTH_URLhttp://localhost:3000NEXT_PUBLIC_CLIENT_PORTAL_URLhttp://clients.localhost:3000ALGOLIA_INDEX_NAMEsearch_entities_dev
Production (.env.production)
Variable Production Value DATABASE_URLProduction database (via Vercel) BETTER_AUTH_URLhttps://charle.agencyNEXT_PUBLIC_BETTER_AUTH_URLhttps://charle.agencyNEXT_PUBLIC_CLIENT_PORTAL_URLhttps://clients.charle.agencyALGOLIA_INDEX_NAMEsearch_entities_prod
Never use production credentials locally - the .env.local file is configured with safe development values. Production credentials are managed via Vercel.
Required vs Optional Variables
Required for Core Functionality
These are required for the app to run:
DATABASE_URL - Database connection
BETTER_AUTH_SECRET - Auth encryption
BETTER_AUTH_URL and public variants - Auth configuration
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET - OAuth sign-in
Optional (Feature-Specific)
These are optional and only needed for specific features:
Pusher - Real-time updates (app works without, but no live updates)
Resend - Email notifications (app works without, but no emails sent)
Algolia - Search (app works without, but search will be slower/limited)
HubSpot - CRM integration (purely optional)
OpenAI - AI features (Alan won’t work without this)
Slack - Slack notifications (purely optional)
Sentry - Error tracking (purely optional, for monitoring)
Cloudflare R2 - File uploads (attachments won’t work without this)
Managing Environment Variables
Local Development
The .env.local file is already configured and checked into the repository. You don’t need to create or modify it unless:
You’re testing a specific integration
You need to use a different database branch
You’re debugging auth issues
Adding New Variables
If you add new environment variables to the codebase:
Add to .env.local with a dev/test value
Add to .env.example with a placeholder
Document here in this page
Add to Vercel for production (via Vercel Dashboard)
Security Best Practices
.env.local is in the repo but only has dev/test credentials
Never commit production API keys or secrets
Production secrets are managed via Vercel environment variables
Use Environment-Specific Values
Development values in .env.local
Production values in Vercel Dashboard
Never mix environments
Rotate Compromised Secrets
If a secret is exposed:
Immediately rotate it in the service (Google, Pusher, etc.)
Update Vercel environment variables
Update .env.local if it’s a dev credential
Redeploy if necessary
Use Public Variables Correctly
Variables prefixed with NEXT_PUBLIC_ are exposed to the client:
Only use for public, non-sensitive data
Examples: NEXT_PUBLIC_PUSHER_KEY (read-only), NEXT_PUBLIC_APP_URL
Never use for API secrets or tokens
Troubleshooting
Missing Variable Errors
Error: “Missing environment variable: X”
Fix:
Check if the variable exists in .env.local
If missing, add it (check .env.example for reference)
Restart the dev server: npm run dev
Wrong Environment
Error: Auth URLs point to production / CORS errors
Fix:
Verify .env.local has BETTER_AUTH_URL=http://localhost:3000
Check you haven’t accidentally modified .env.local with production values
Restore from git if needed: git checkout .env.local
Integration Not Working
If an integration (Pusher, Algolia, etc.) isn’t working:
Check the variable is set in .env.local
Verify the credential is valid in the service’s dashboard
Restart the dev server - env vars are loaded on startup
Check service status - the external service might be down