configuration - nself-org/nchat GitHub Wiki
nchat is highly configurable through environment variables and the AppConfig system. This guide covers all configuration options.
- Environment Variables
- AppConfig System
- Backend Configuration
- Feature Configuration
- Theme Configuration
- Security Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_GRAPHQL_URL |
Yes | - | Hasura GraphQL endpoint |
NEXT_PUBLIC_AUTH_URL |
Yes | - | Authentication service URL |
NEXT_PUBLIC_STORAGE_URL |
Yes | - | File storage service URL |
NEXT_PUBLIC_ENV |
No | development |
Environment mode |
PORT |
No | 3000 |
Server port |
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_USE_DEV_AUTH |
No | false |
Enable development auth |
NEXTAUTH_SECRET |
Prod | - | NextAuth.js secret key |
NEXTAUTH_URL |
Prod | - | Application URL |
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID |
Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret |
| Variable | Description |
|---|---|
GITHUB_CLIENT_ID |
GitHub OAuth app ID |
GITHUB_CLIENT_SECRET |
GitHub OAuth app secret |
| Variable | Description |
|---|---|
APPLE_CLIENT_ID |
Apple Services ID |
APPLE_CLIENT_SECRET |
Apple client secret (JWT) |
APPLE_TEAM_ID |
Apple Developer Team ID |
APPLE_KEY_ID |
Apple Sign-In key ID |
| Variable | Description |
|---|---|
IDME_CLIENT_ID |
ID.me OAuth client ID |
IDME_CLIENT_SECRET |
ID.me OAuth client secret |
IDME_SCOPE |
ID.me verification scopes |
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_APP_NAME |
No | nchat |
Application name |
NEXT_PUBLIC_PRIMARY_COLOR |
No | #6366f1 |
Primary brand color |
NEXT_PUBLIC_LOGO_URL |
No | - | Logo image URL |
NEXT_PUBLIC_FAVICON_URL |
No | - | Favicon URL |
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_ENABLE_CHANNELS |
true |
Enable channels |
NEXT_PUBLIC_ENABLE_DM |
true |
Enable direct messages |
NEXT_PUBLIC_ENABLE_THREADS |
true |
Enable threading |
NEXT_PUBLIC_ENABLE_REACTIONS |
true |
Enable reactions |
NEXT_PUBLIC_ENABLE_FILE_UPLOADS |
true |
Enable file uploads |
NEXT_PUBLIC_ENABLE_SEARCH |
true |
Enable search |
NEXT_PUBLIC_ENABLE_NOTIFICATIONS |
true |
Enable notifications |
# Backend URLs
NEXT_PUBLIC_GRAPHQL_URL=http://api.localhost/v1/graphql
NEXT_PUBLIC_AUTH_URL=http://auth.localhost/v1/auth
NEXT_PUBLIC_STORAGE_URL=http://storage.localhost/v1/storage
# Environment
NEXT_PUBLIC_ENV=development
NEXT_PUBLIC_USE_DEV_AUTH=true
# Branding
NEXT_PUBLIC_APP_NAME=My Chat App
NEXT_PUBLIC_PRIMARY_COLOR=#0ea5e9
# Features
NEXT_PUBLIC_ENABLE_CHANNELS=true
NEXT_PUBLIC_ENABLE_DM=true
NEXT_PUBLIC_ENABLE_THREADS=trueThe AppConfig system provides runtime configuration stored in the database.
interface AppConfig {
setup: {
completed: boolean
completedSteps: string[]
currentStep: number
}
owner: {
name: string
email: string
company: string
role: string
}
branding: {
appName: string
logo: string | null
favicon: string | null
tagline: string
companyName: string
}
landing: {
mode: 'landing' | 'redirect' | 'chat'
heroTitle: string
heroSubtitle: string
showFeatures: boolean
showTestimonials: boolean
}
auth: {
methods: {
email: boolean
google: boolean
github: boolean
apple: boolean
idme: boolean
}
permissions: 'allow-all' | 'verified-only' | 'admin-only'
requireEmailVerification: boolean
allowRegistration: boolean
}
features: {
channels: boolean
directMessages: boolean
threads: boolean
reactions: boolean
fileUploads: boolean
voiceMessages: boolean
videoCall: boolean
screenShare: boolean
polls: boolean
reminders: boolean
search: boolean
notifications: boolean
bots: boolean
webhooks: boolean
}
theme: {
mode: 'light' | 'dark' | 'system'
preset: string
colors: {
primary: string
secondary: string
accent: string
background: string
foreground: string
}
}
integrations: {
slack: { enabled: boolean; webhookUrl?: string }
github: { enabled: boolean; token?: string }
jira: { enabled: boolean; domain?: string }
googleDrive: { enabled: boolean }
}
}import { useAppConfig } from '@/contexts/app-config-context'
function MyComponent() {
const { config, updateConfig, isLoading } = useAppConfig()
if (isLoading) return <Loading />
return (
<div>
<h1>{config.branding.appName}</h1>
<p>{config.branding.tagline}</p>
</div>
)
}const { updateConfig } = useAppConfig()
// Update branding
await updateConfig({
branding: {
appName: 'New Name',
tagline: 'New tagline',
},
})
// Update features
await updateConfig({
features: {
polls: true,
voiceMessages: true,
},
})AppConfig is stored in two locations:
- localStorage - For fast initial load
- Database - As source of truth
The system loads from localStorage first, then syncs with the database.
The backend is configured via .backend/.env:
# Database
POSTGRES_PASSWORD=secret
POSTGRES_DB=nchat
# Hasura
HASURA_GRAPHQL_ADMIN_SECRET=your-admin-secret
HASURA_GRAPHQL_JWT_SECRET={"type":"HS256","key":"your-jwt-key"}
# Auth
AUTH_JWT_SECRET=your-jwt-secret
AUTH_SMTP_HOST=smtp.example.com
AUTH_SMTP_PORT=587
AUTH_SMTP_USER=user
AUTH_SMTP_PASS=password
# Storage
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio123Enable or disable services in .backend/.env:
# Required services (always enabled)
# - postgres
# - hasura
# - auth
# - nginx
# Optional services
NSELF_ADMIN_ENABLED=true
MINIO_ENABLED=true
REDIS_ENABLED=true
FUNCTIONS_ENABLED=false
MAILPIT_ENABLED=true
MEILISEARCH_ENABLED=false
MLFLOW_ENABLED=false
# Monitoring bundle
MONITORING_ENABLED=falseconst channelConfig = {
maxChannels: 100,
maxMembersPerChannel: 1000,
allowPrivateChannels: true,
allowChannelCreation: 'all' | 'admin' | 'none',
defaultChannels: ['general', 'random'],
archiveInactiveAfterDays: 90,
}const messageConfig = {
maxLength: 10000,
editTimeWindow: 3600, // seconds
deleteTimeWindow: 86400, // seconds
allowedMimeTypes: ['image/*', 'video/*', 'application/pdf'],
maxFileSize: 50 * 1024 * 1024, // 50MB
maxFilesPerMessage: 10,
}const notificationConfig = {
enablePush: true,
enableEmail: true,
enableDesktop: true,
quietHours: {
enabled: true,
start: '22:00',
end: '08:00',
timezone: 'America/New_York',
},
grouping: {
enabled: true,
windowSeconds: 60,
},
}nchat includes 8+ theme presets:
| Preset | Description |
|---|---|
default |
Clean, professional look |
slack |
Slack-inspired colors |
discord |
Discord dark theme |
telegram |
Telegram blue theme |
whatsapp |
WhatsApp green theme |
midnight |
Dark mode with purple accents |
ocean |
Blue ocean theme |
forest |
Green nature theme |
sunset |
Warm orange theme |
const customTheme = {
mode: 'dark',
preset: 'custom',
colors: {
primary: '#6366f1',
secondary: '#8b5cf6',
accent: '#ec4899',
background: '#0f172a',
foreground: '#f8fafc',
muted: '#64748b',
border: '#334155',
error: '#ef4444',
success: '#22c55e',
warning: '#f59e0b',
},
fonts: {
sans: 'Inter, sans-serif',
mono: 'JetBrains Mono, monospace',
},
radius: {
sm: '0.25rem',
md: '0.5rem',
lg: '0.75rem',
},
}import { useTheme } from '@/contexts/theme-context'
function ThemeSwitcher() {
const { theme, setTheme, presets } = useTheme()
return (
<select
value={theme.preset}
onChange={(e) => setTheme({ preset: e.target.value })}
>
{presets.map((preset) => (
<option key={preset.id} value={preset.id}>
{preset.name}
</option>
))}
</select>
)
}const authSecurity = {
password: {
minLength: 8,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialChars: false,
},
session: {
maxAge: 30 * 24 * 60 * 60, // 30 days
updateAge: 24 * 60 * 60, // 24 hours
},
rateLimit: {
login: {
window: 15 * 60, // 15 minutes
max: 5,
},
register: {
window: 60 * 60, // 1 hour
max: 3,
},
},
}const contentSecurity = {
sanitize: {
html: true,
markdown: true,
links: true,
},
allowedHtml: ['b', 'i', 'u', 'a', 'code', 'pre'],
linkPreview: {
enabled: true,
timeout: 5000,
},
profanityFilter: {
enabled: false,
action: 'blur' | 'replace' | 'block',
},
}In production, configure CORS in .backend/nginx.conf:
location /v1/graphql {
add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
}Configuration is loaded in this order (later overrides earlier):
- Default values (hardcoded)
- Environment variables
-
.env.localfile - AppConfig from database
- Runtime updates