Environment & Configuration Reference¶
This document is the canonical reference for how the mr-mentor-backend service is configured: every environment variable it reads, the three-tier config-resolution model (process env -> runtime SystemConfig table -> build-time feature flags), how config differs between local / dev / prod, and which secrets must never be committed. It is the operations-facing companion to the per-feature docs that describe what each subsystem does.
Status: documented from source on this branch. Variable names, defaults and code paths below were derived by reading
.env.example, grepping everyprocess.env.*reference undersrc/, and readingsrc/utils/featureFlags.ts,src/services/SystemConfigService.ts,src/entities/SystemConfig.ts,src/config/database.tsandsrc/config/redis.ts.
Overview¶
mr-mentor-backend is a single Express + TypeScript service (port 8000) that hosts many products (Mr. Mentor mentorship, Mr. Hire ATS, MAS LMS, Sales CRM, Finance, AI Platform). Because every product lives in one process, all of their configuration is loaded from one flat environment plus a handful of runtime-editable rows in the database.
Configuration is resolved at three layers, in increasing order of "operator can change it without a deploy":
| Layer | Source | Who edits it | Takes effect |
|---|---|---|---|
| 1. Process env | .env locally; AWS Secrets Manager in dev/prod |
Engineers / DevOps | On process (re)start |
| 2. Runtime config | system_configs table via SystemConfigService |
Admins via API/UI | Immediately (next read) |
| 3. Feature flags | process.env.ENABLE_* read through src/utils/featureFlags.ts |
Engineers / DevOps (kill switch) | On process restart |
Personas who touch config:
- DevOps / release engineers — manage Secrets Manager secrets, set kill-switch flags, rotate credentials.
- Admins / Superadmins — flip runtime system_configs rows (e.g. global_auto_screen_on_apply) and store encrypted integration credentials (Leegality fallback) through the admin API.
- Engineers — add new env vars (see the add-env-var skill workflow) and feature flags.
Key concepts & entities¶
| Concept | Meaning |
|---|---|
| Process env var | process.env.FOO. Read once per access; not hot-reloadable. Loaded from .env (local) or injected from AWS Secrets Manager at container start (dev/prod). |
| Feature flag | A boolean gate read through readFlag() in src/utils/featureFlags.ts. Default ON — only the literal strings "false" or "0" turn it OFF. Acts as an emergency kill switch. |
Runtime config (SystemConfig) |
A key/value/label/description row in system_configs, editable live via admin endpoints. Used for cross-service flags (read by mr-hire-backend on startup) and for encrypted credential fallbacks. |
| Secret | Any credential, token, key or password. Must live in AWS Secrets Manager (dev/prod) and never be committed. |
x-platform header |
Multi-tenant routing header (mr-mentor / my-analytics-school). Affects behaviour, not env loading; the same process serves all platforms. |
Main config-related entities (file paths):
- src/entities/SystemConfig.ts — the system_configs table (id, key UNIQUE, value text, label, description, isActive, timestamps).
- src/services/SystemConfigService.ts — getAll / getByKey / getValue / upsert / update / delete.
- src/utils/featureFlags.ts — the FeatureFlags object (5 flags).
- src/config/database.ts — TypeORM DataSource (reads DB_*, defaults localhost/shubham/template).
- src/config/redis.ts — ioredis singleton (reads REDIS_HOST/REDIS_PORT, defaults localhost/6379).
Architecture¶
flowchart TD
subgraph SRC["Config sources"]
ENV[".env file (local only)"]
SM["AWS Secrets Manager (dev/prod)"]
DBROW["system_configs table"]
end
ENV -->|"loaded at startup"| PROC["process.env"]
SM -->|"injected at container start"| PROC
PROC --> DBCFG["src/config/database.ts (DB_* with defaults)"]
PROC --> REDISCFG["src/config/redis.ts (REDIS_* with defaults)"]
PROC --> FF["src/utils/featureFlags.ts readFlag"]
PROC --> SVCS["Services and controllers (direct process.env reads)"]
DBROW --> SCS["SystemConfigService"]
SCS --> SVCS
DBCFG --> APP["Express app + workers + Socket.IO"]
REDISCFG --> APP
FF --> APP
SVCS --> APP
APP -->|"behaviour"| EXT["External systems: Razorpay, S3, Groq, LiteLLM, Exotel, WhatsApp, Leegality, LiveKit, Twilio, ElevenLabs"]
Data model¶
erDiagram
SYSTEM_CONFIG {
uuid id PK
varchar key UK
text value
varchar label
varchar description
boolean isActive
timestamp createdAt
timestamp updatedAt
}
system_configs is the only config-owned table. Notable in-use keys (created lazily via upsert):
| Key | Written/read by | Notes |
|---|---|---|
global_auto_screen_on_apply |
src/controllers/mrHire.controller.ts |
"true"/"false" — auto-run resume screening when a candidate applies. |
mas101_leegality_sandbox_base_url |
src/services/LeegalitySandboxService.ts |
Encrypted fallback for LEEGALITY_SANDBOX_BASE_URL. |
mas101_leegality_sandbox_auth_token |
src/services/LeegalitySandboxService.ts |
Encrypted fallback auth token. |
mas101_leegality_sandbox_create_request_template |
src/services/LeegalitySandboxService.ts |
Encrypted create-request JSON template. |
SystemConfigService.getByKey only returns rows where isActive = true; setting isActive = false is a soft disable.
API surface¶
Mounted under /api (see src/routes/index.ts line 401 -> SystemConfigRoutes). Defined in src/routes/systemConfig.routes.ts.
| Method | Path | Auth/role | Purpose |
|---|---|---|---|
| GET | /api/system-config/enabled-call-providers |
Public | HR call-provider config (handled by HrCallConfigController.getEnabledProviders). |
| GET | /api/system-config/:key |
Public | Read a single config value. Intentionally open — mr-hire-backend reads it on startup. Returns only isActive rows. |
| POST | /api/admin/system-config/refresh-mr-hire |
authMiddleware |
Push provider config refresh to Mr. Hire backend. |
| GET | /api/admin/system-config |
authMiddleware |
List all config rows (ordered by key). |
| POST | /api/admin/system-config |
authMiddleware |
Upsert a config (key, value, label, description). |
| PUT | /api/admin/system-config/:id |
authMiddleware |
Update a config row by id. |
| DELETE | /api/admin/system-config/:id |
authMiddleware |
Hard-delete a config row. |
Route ordering note: specific paths are registered before the parameterized
/:keyso they are not shadowed.
There is no API for env vars or feature flags — those change only via the deploy/secrets pipeline plus a restart.
Environment variable reference¶
Comprehensive grouped table of every process.env.* referenced under src/. "Required?" reflects whether the related feature breaks without it; many are optional and disable their feature gracefully. No real values are printed — examples are placeholders or the in-code defaults.
server¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
PORT |
HTTP listen port | No | 8000 |
NODE_ENV |
Environment mode (development / production) |
No | development |
HOST |
Bind host (where referenced) | No | — |
TZ |
Process timezone | No | — |
BASE_URL |
App base URL (internal links) | No | — |
PUBLIC_BASE_URL |
Public-facing base URL | No | — |
BACKEND_PUBLIC_URL |
Public API URL for webhook callbacks (e.g. Exotel) | No (yes if Exotel enabled) | https://api.mrmentor.in |
CORS_ALLOWED_ORIGINS |
JSON array of allowed origins | No | ["http://localhost:3000",...] |
BULL_BOARD_USERNAME |
Basic-auth user for /admin/queues |
No | — |
BULL_BOARD_PASSWORD |
Basic-auth password for /admin/queues |
Secret | — |
ENABLE_SEEDING |
Seed batches/courses on startup | No | false |
RUN_SEED_DIRECTLY |
Run a seed script directly | No | — |
DRY_RUN |
Dry-run mode for scripts/jobs | No | — |
DEBUG_AI_RECS |
Verbose AI-recommendation logging | No | — |
database¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
DB_HOST |
PostgreSQL host | No (defaulted) | localhost |
DB_PORT |
PostgreSQL port | No (defaulted) | 5432 |
DB_USERNAME |
DB user | No (defaulted) | shubham |
DB_PASSWORD |
DB password | Secret | shubham (dev only) |
DB_NAME |
Database name | No (defaulted) | template (in code) / mas (deployed) |
src/config/database.tshard-codes the defaults above (lines 176-181, 388-419). TypeORM auto-sync is ON — entity changes apply without migrations in dev.
redis¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
REDIS_HOST |
Redis host | No (defaulted) | localhost |
REDIS_PORT |
Redis port | No (defaulted) | 6379 |
REDIS_PASSWORD |
Redis auth (referenced; commented in redis.ts) |
Secret | — |
REDIS_DB |
Redis DB index (referenced; commented in redis.ts) |
No | 0 |
auth / jwt / google¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
JWT_SECRET |
Signs/verifies user JWTs | Yes — Secret | — |
GOOGLE_CLIENT_ID |
Google OAuth client id | Yes (for Google login) | — |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret | Secret | — |
GOOGLE_REDIRECT_URI |
OAuth callback URL | Yes (Google login) | http://localhost:3000/api/auth/callback/google |
GOOGLE_DRIVE_REDIRECT_URI |
Google Drive connector callback | No | http://localhost:8000/api/google-drive-connector/callback |
ADMIN_EMAIL |
Bootstrap admin account email | Yes (first boot) | admin@example.com |
ADMIN_PASSWORD |
Bootstrap admin password | Secret | — |
MASTER_PASSWORD |
Login-as-any-user master password (with TOTP). Empty disables. | Secret, optional | empty |
MASTER_TOTP_SECRET |
Base32 TOTP secret pairing with master password | Secret, optional | empty |
INTERNAL_SERVICE_TOKEN |
Shared token for internal service-to-service calls | Secret | — |
QUIZ_TOKEN_SECRET |
Signs candidate-quiz access tokens | Secret | — |
SYSTEM_OTP_SENDER_USER_ID |
User id used as sender for system OTPs | No | — |
email¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
EMAIL_USER |
Gmail SMTP user | Yes (email) | — |
EMAIL_PASS |
Gmail app password | Secret | — |
EMAIL_FROM |
From address on outgoing mail | No | noreply@yourapp.com |
EMAIL_TIMEOUT_MS |
SMTP send timeout | No | 8000 |
MENTOR_ONBOARDING_CC_EMAIL |
CC on mentor onboarding mails | No | info@myanalyticsschool.com |
STUDENT_ONBOARDING_CC_EMAIL |
CC on student onboarding mails | No | info@myanalyticsschool.com |
If Gmail returns
535-5.7.8 BadCredentials, rotate the app password and update both SM secrets, then restart blue+green and retry stuck BullMQ jobs (they do not auto-retry).
S3 / storage¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
AWS_REGION |
AWS region | Yes (S3) | ap-south-1 (deployed) / us-east-1 (example) |
AWS_ACCESS_KEY_ID |
AWS access key | Secret | — |
AWS_SECRET_ACCESS_KEY |
AWS secret key | Secret | — |
AWS_S3_BUCKET_NAME |
Meeting-recordings bucket | Yes (recordings) | mr-mentor-recordings |
AWS_S3_STUDENT_DOCUMENTS_BUCKET |
Student documents bucket | No | myanalytics-documents |
AWS_S3_BANNER_ASSETS_BUCKET |
Banner assets bucket | No | myanalyticsschool-assets |
AWS_S3_COURSES_BUCKET |
Course content bucket | No | — |
MISS_OZONE_ARCHIVE_BUCKET |
MissOzone voice-interview archive bucket | No | — |
MAS_ASSETS_CDN_URL |
CDN base for assets | No | — |
Razorpay / payments¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
RAZORPAY_KEY_ID |
Razorpay key id | Yes (payments) | — |
RAZORPAY_KEY_SECRET |
Razorpay key secret | Secret | — |
TOKEN_VALUE |
INR value of one token | No | 350 |
meetings¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
ALLOW_EARLY_MEETING_JOIN |
Allow joining before scheduled time | No | true (dev) |
MEETING_JOIN_BUFFER_MINUTES |
Minutes before start users may join | No | 5 |
MAX_FREE_MEETINGS_PER_STUDENT |
Cap on free meetings | No | — |
MAX_SLOT_REQUESTS |
Cap on outstanding slot requests | No | — |
APPLICATION_MEETINGS_EXPIRY_DATE |
Cut-off date for application-meeting flow | No | — |
APPLICATION_MEETING_DURATION_MINUTES |
Application meeting length | No | — |
APPLICATION_MEETING_TIME_ZONE |
TZ for application meetings | No | — |
APPLICATION_MEETING_REMINDER_JWT_TTL |
Reminder link JWT TTL | No | — |
APPLICATION_MEETING_REMINDER_LEAD_MINUTES |
Minutes before meeting to send reminder | No | — |
APPLICATION_MEETING_REMINDER_PHONE_NUMBER_ID |
WhatsApp phone-number id for reminders | No | — |
APPLICATION_MEETING_REMINDER_TEMPLATE_NAME |
Reminder WhatsApp template | No | — |
APPLICATION_MEETING_REMINDER_TEMPLATE_LANGUAGE |
Reminder template language | No | — |
APPLICATION_MEETING_REMINDER_URL_PREFIX |
URL prefix in reminder | No | — |
LIVEKIT_API_BASE |
LiveKit voice-agent base URL | No | http://mas-voice-agent-web:5001 |
LIVEKIT_WEBHOOK_SECRET |
LiveKit webhook signature secret | Secret | — |
integration URLs (cross-repo)¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
FRONTEND_URL |
mr-mentor-frontend base URL | Yes | http://localhost:3000 |
MR_HIRE_FRONTEND_URL |
mr-hire-frontend base URL | No | http://localhost:5173 |
MR_HIRE_BACKEND_URL |
mr-hire-backend base URL | Yes (Mr. Hire) | http://localhost:8001 |
MR_HIRE_WEBHOOK_SECRET |
Shared secret for Mr. Hire webhooks | Secret | — |
MR_HIRE_CALL_GAP |
Min gap between AI calls | No | — |
MAS_WEBSITE_URL |
Public marketing site URL | No | https://www.myanalyticsschool.com |
CLASS_AGENT_URL |
mas-class-agent base URL | No | — |
CLASS_AGENT_API_KEY |
Auth key for class-agent (X-Internal-API-Key) |
Secret | — |
GRAPHY_BASE_URL |
Graphy LMS (Mr. Learn) base URL | No | — |
EZEXAM_BASE_URL |
Mr. Test (EzExam) base URL | No | — |
JUDGE0_API_URL |
Judge0 code-execution API | No | — |
Exotel (telephony — CRM click-to-call + SMS)¶
All optional; the feature disables itself (falls back to tel: links) when unset.
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
EXOTEL_SID |
Exotel account SID | No | — |
EXOTEL_API_KEY |
Exotel API key | Secret | — |
EXOTEL_API_TOKEN |
Exotel API token | Secret | — |
EXOTEL_CALLER_ID |
ExoPhone virtual number | No | — |
EXOTEL_SUBDOMAIN |
API cluster subdomain — must match the account's cluster or auth 401s | No | api.exotel.com (SG) / api.in.exotel.com (Mumbai) |
EXOTEL_SMS_SENDER_ID |
DLT sender id (falls back to ExoPhone) | No | — |
EXOTEL_WEBHOOK_TOKEN |
Shared secret for /api/exotel/* status callbacks |
Secret | — |
WhatsApp¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
WHATSAPP_PROVIDER |
meta_cloud or aisensy |
No | meta_cloud |
WHATSAPP_ACCESS_TOKEN |
Provider access token | Secret | — |
WHATSAPP_PHONE_NUMBER_ID |
Sender phone-number id | No | — |
WHATSAPP_BUSINESS_ACCOUNT_ID |
WABA id | No | — |
WHATSAPP_GRAPH_API_VERSION |
Meta Graph API version | No | v17.0 |
WHATSAPP_WEBHOOK_VERIFY_TOKEN |
Webhook verification token | Secret | — |
WHATSAPP_OTP_TEMPLATE_NAME |
OTP template name | No | — |
WHATSAPP_OTP_TEMPLATE_LANG |
OTP template language | No | — |
WHATSAPP_CREDENTIALS_ENCRYPTION_KEY |
Encrypts stored WhatsApp creds | Secret | — |
AI / LLM¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
GROQ_API_KEY |
Groq LLM key for "Ask MAS" — endpoints return 503 until set | Secret, optional | — |
GROQ_TEXT_MODEL |
Groq model id | No | llama-3.3-70b-versatile |
GROQ_TEXT_ENDPOINT |
Groq chat completions URL | No | https://api.groq.com/openai/v1/chat/completions |
GROQ_TIMEOUT_MS |
Upstream timeout | No | 25000 |
LITELLM_BASE_URL |
LiteLLM gateway base URL | No | http://mas-llm-gateway:4000 (internal) |
LITELLM_MASTER_KEY |
LiteLLM master key | Secret | — |
LITELLM_ADMIN_TIMEOUT_MS |
LiteLLM admin call timeout | No | — |
LLM_GATEWAY_ENCRYPTION_KEY |
Encrypts per-user LLM keys | Secret | — |
ASKMAS_GATEWAY_MODEL |
Default Ask-MAS gateway model | No | — |
ASKMAS_USER_MONTHLY_BUDGET |
Per-user monthly LLM budget | No | — |
ASKMAS_USER_BUDGET_DURATION |
Budget reset window | No | — |
ASKMAS_USER_RPM |
Per-user requests/min | No | — |
ASKMAS_USER_TPM |
Per-user tokens/min | No | — |
ELEVENLABS_API_KEY |
ElevenLabs voice-interview key | Secret | — |
AARYA_AGENT_ID |
Aarya AI-calling agent id | No | — |
AARYA_PHONE_NUMBER_ID |
Aarya phone-number id | No | — |
TWILIO_ACCOUNT_SID |
Twilio account SID (voice/SMS) | No | — |
TWILIO_AUTH_TOKEN |
Twilio auth token | Secret | — |
Leegality (e-signing / PAP agreements)¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
LEEGALITY_SANDBOX_BASE_URL |
Leegality sandbox base URL (env fallback for DB config) | No | — |
LEEGALITY_SANDBOX_AUTH_TOKEN |
Leegality sandbox auth token | Secret | — |
MAS101_LEEGALITY_SANDBOX_CREATE_REQUEST_TEMPLATE |
Create-request JSON template | No | — |
MAS101_PAP_TEMPLATE_PATH |
MAS101 PAP MOU template file path | No | — |
MAS101_PAP_DEMO_AUTOVERIFY |
Auto-verify PAP eSign in demo | No | — |
Leegality config has a two-tier fallback:
LeegalitySandboxServicereads env first, then falls back to the encryptedmas101_leegality_sandbox_*rows insystem_configs(decrypted withgetEncryptionKey()).
Finance / GST invoicing (GLOBAL_INVOICE_*)¶
All optional with sensible code defaults; required only when the invoice module is live.
| Variable | Purpose | Default / example |
|---|---|---|
GLOBAL_INVOICE_S3_BUCKET |
Secured bucket for invoice PDFs | — |
GLOBAL_INVOICE_S3_PREFIX |
Key prefix | tax-invoices |
GLOBAL_INVOICE_FINANCE_EMAIL |
CC finance on issued invoices | — |
GLOBAL_INVOICE_ADMIN_EMAIL |
CC admin | admin@myanalyticsschool.com |
GLOBAL_INVOICE_BANK_ACCOUNT_NAME / _BANK_NAME / _BANK_ACCOUNT_NUMBER / _BANK_IFSC / _BANK_BRANCH |
Bank block on PDF | — |
GLOBAL_INVOICE_TERMS |
Pipe-separated T&C lines | — |
GLOBAL_INVOICE_MAS101_PREFIX / _MAS102_PREFIX / _PAP_PREFIX / _AERO_PREFIX / _DRO_PREFIX |
Invoice series prefixes | MAS101 etc. |
GLOBAL_INVOICE_MAS_PREFIX |
Legacy — no-op, code reads MAS101_PREFIX |
MAS |
GLOBAL_INVOICE_PAP_FORMAT / _MAS_ITEM / _PAP_ITEM |
Number format + line-item labels | — |
GLOBAL_INVOICE_HSN_SAC |
HSN/SAC code | 999293 |
GLOBAL_INVOICE_GST_RATE |
GST percent | 18 |
GLOBAL_INVOICE_SUPPLIER_NAME / _LEGAL_NAME / _GSTIN / _STATE / _STATE_CODE / _ADDRESS_LINE1 / _ADDRESS_LINE2 / _EMAIL / _PHONE / _WEBSITE |
Supplier (MAS) details on PDF | — |
Vendor API platform & encryption keys / misc¶
| Variable | Purpose | Required? | Default / example |
|---|---|---|---|
VENDOR_API_KEY_PEPPER |
Pepper for hashing vendor API keys | Secret | — |
PLATFORM_ENCRYPTION_KEY |
Encrypts platform/MasGateway secrets |
Secret | — |
STARTER_BUNDLE_MOCK |
Mock token count in starter bundle | No | — |
STARTER_BUNDLE_RESUME |
Resume-review token count in starter bundle | No | — |
Feature-flag env vars (kill switches)¶
| Variable | Default | Set to disable |
|---|---|---|
ENABLE_TOKEN_REQUEST_FLOW |
ON | false / 0 |
ENABLE_RESUME_REVIEW_SPEND |
ON | false / 0 |
ENABLE_STARTER_BUNDLE |
ON (reserved) | false / 0 |
ENABLE_GRANT_AUDIT |
ON (reserved) | false / 0 |
ENABLE_APPLICATION_MEETINGS |
ON | false / 0 |
ENABLE_SEEDING |
OFF (false) |
n/a — set true to enable |
Feature flags¶
src/utils/featureFlags.ts exposes the FeatureFlags object. Each flag is a function evaluated on every call (so the value is read live from process.env, but env itself does not change without a restart). Semantics (readFlag):
value === "false" -> OFF
value === "0" -> OFF
anything else -> ON (unset, "", "true", "1", "yes", ...)
This "default ON, explicit kill switch" design means new governance features activate automatically on deploy; an operator disables one in an emergency by setting the env var to false and restarting / pm2 reload.
flowchart TD
A["Code calls FeatureFlags.x"] --> B{"process.env.ENABLE_X"}
B -->|"false or 0"| OFF["Feature OFF"]
B -->|"unset / true / 1 / other"| ON["Feature ON"]
Five flags exist; starterBundle and grantAudit are reserved (not yet consumed by any code path).
Config-resolution flow¶
End-to-end resolution for a single setting, from raw env through runtime override to behaviour:
flowchart TD
START["Request needs a setting"] --> Q1{"Is it a kill-switch feature?"}
Q1 -->|"Yes"| FF["readFlag ENABLE_X"]
FF --> Q1B{"value is false or 0?"}
Q1B -->|"Yes"| BLOCK["Skip feature, return early"]
Q1B -->|"No"| RUN["Run feature"]
Q1 -->|"No"| Q2{"Has a runtime override in system_configs?"}
Q2 -->|"Yes, isActive"| SC["SystemConfigService.getValue key"]
SC --> Q2B{"Encrypted credential?"}
Q2B -->|"Yes"| DEC["decryptSecret with encryption key"]
Q2B -->|"No"| USEVAL["Use stored value"]
DEC --> USEVAL
Q2 -->|"No row"| ENVCHK{"process.env set?"}
ENVCHK -->|"Yes"| USEENV["Use env value"]
ENVCHK -->|"No"| DEF["Use in-code default or disable feature gracefully"]
USEVAL --> BEHAVE["Behaviour"]
USEENV --> BEHAVE
DEF --> BEHAVE
RUN --> BEHAVE
Concrete examples:
- Leegality base URL — LeegalitySandboxService tries process.env.LEEGALITY_SANDBOX_BASE_URL, else decrypts mas101_leegality_sandbox_base_url from system_configs.
- Auto-screen on apply — mrHire.controller reads runtime global_auto_screen_on_apply; no env equivalent.
- DB connection — database.ts reads DB_* or falls back to hard-coded localhost/shubham/template defaults.
- Ask MAS — if GROQ_API_KEY is unset, the endpoint returns 503 (graceful disable) rather than erroring.
Environment differences: local / dev / prod¶
| Aspect | Local | Dev | Prod |
|---|---|---|---|
| Env source | .env file (copy of .env.example) |
AWS Secrets Manager mr-mentor-backend/development (ap-south-1, profile projects) |
AWS Secrets Manager mr-mentor-backend/production |
NODE_ENV |
development |
development/production |
production |
| DB | local Postgres mas, creds shubham/shubham |
dev RDS/host | prod DB |
ALLOW_EARLY_MEETING_JOIN |
true |
usually true |
false (enforce buffer) |
| Exotel cluster | SG account (api.exotel.com) |
SG account | Mumbai account SID myanalyticsschool1m, api.in.exotel.com |
| LLM gateway | optional / off | http://mas-llm-gateway:4000 internal + https://dev-llm.myanalyticsschool.com |
prod gateway |
| Invoice module | off / placeholder | test bucket | live bucket, ICICI bank, real GSTIN |
| Deploy | npm run dev |
Docker on dev EC2 | blue-green Docker on prod EC2 (push to main auto-builds) |
Adding a new env var? Use the
add-env-varskill: it propagates the var to local.env, theenvs/mirror, the relevant secret store (AWS SM for this backend) and the deployed server. Do not hand-edit only one location.
Background jobs & async¶
Config affects background processing but the config domain itself owns no jobs. Relevant interactions:
- BullMQ workers (email, database, cleanup, kpi, resumeAnalysis) connect via REDIS_HOST/REDIS_PORT. Cleanup runs every 24h, KPI every 15min (src/index.ts startup sequence).
- /admin/queues Bull Board UI is protected by BULL_BOARD_USERNAME / BULL_BOARD_PASSWORD.
- Startup seeding is gated by ENABLE_SEEDING (and RUN_SEED_DIRECTLY for direct script runs).
- Webhooks validated by env secrets: EXOTEL_WEBHOOK_TOKEN, MR_HIRE_WEBHOOK_SECRET, LIVEKIT_WEBHOOK_SECRET, WHATSAPP_WEBHOOK_VERIFY_TOKEN.
External integrations¶
| Integration | Key vars | Failure / fallback |
|---|---|---|
| Razorpay | RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET |
Payment routes fail if unset. |
| AWS S3 | AWS_*, bucket vars |
Upload/recording features fail if creds/region missing. |
| Gmail SMTP | EMAIL_USER, EMAIL_PASS |
535-5.7.8 -> rotate app password in both SM secrets, restart, retry jobs. |
| Groq (Ask MAS) | GROQ_API_KEY (+ model/endpoint/timeout) |
Endpoints return 503 until key set. |
| LiteLLM gateway | LITELLM_BASE_URL, LITELLM_MASTER_KEY, LLM_GATEWAY_ENCRYPTION_KEY |
Per-user LLM keys encrypted with gateway key. |
| Exotel | EXOTEL_* |
Disables itself; CRM falls back to tel: links. EXOTEL_SUBDOMAIN must match cluster or 401. |
WHATSAPP_* |
Provider switch meta_cloud/aisensy; creds encrypted with WHATSAPP_CREDENTIALS_ENCRYPTION_KEY. |
|
| Leegality | LEEGALITY_SANDBOX_*, MAS101_* |
Env -> encrypted system_configs fallback. |
| LiveKit / voice agents | LIVEKIT_API_BASE, LIVEKIT_WEBHOOK_SECRET, ELEVENLABS_API_KEY, AARYA_* |
Internal-only on mas-network. |
| Twilio | TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN |
Used for AI phone calling. |
| Judge0 | JUDGE0_API_URL |
Code-execution for browser terminal/editor. |
| Graphy (Mr. Learn) / EzExam (Mr. Test) | GRAPHY_BASE_URL, EZEXAM_BASE_URL |
Proxy integrations; sync via background jobs. |
| mas-class-agent | CLASS_AGENT_URL, CLASS_AGENT_API_KEY |
Backend key must equal agent INTERNAL_API_KEY or 401. |
Status lifecycles¶
SystemConfig rows have a soft-active lifecycle (isActive), not a rich status machine:
stateDiagram-v2
[*] --> Active: upsert creates row (isActive default true)
Active --> Active: update value
Active --> Inactive: set isActive false
Inactive --> Active: set isActive true
Active --> Deleted: DELETE by id
Inactive --> Deleted: DELETE by id
Deleted --> [*]
getByKey / getValue return null for Inactive rows, so deactivating a key behaves like "unset" without losing the stored value.
Edge cases, limits & gotchas¶
- Feature flags default ON. Forgetting to set
ENABLE_X=falsemeans the feature ships live. The only OFF values are"false"and"0"—"FALSE","no","off"all read as ON. - DB defaults are real. If
DB_*are missing, the service silently connects tolocalhost/shubham/shubham/template— a misconfigured prod could connect somewhere unexpected rather than failing loudly. /api/system-config/:keyis public by design — Mr. Hire reads it at startup. Never store secrets there in plaintext; Leegality fallbacks are stored encrypted.- Encryption keys are load-bearing.
LLM_GATEWAY_ENCRYPTION_KEY,WHATSAPP_CREDENTIALS_ENCRYPTION_KEY,PLATFORM_ENCRYPTION_KEY,VENDOR_API_KEY_PEPPER— rotating any of these without re-encrypting stored ciphertext breaks decryption. GLOBAL_INVOICE_MAS_PREFIXis a no-op — the code readsGLOBAL_INVOICE_MAS101_PREFIX. Easy to set the wrong one.EXOTEL_SUBDOMAINmust match the account cluster (SG vs Mumbai) or every call/SMS 401s.- Secrets must never be committed.
.env.exampleships placeholders only. Real values live in AWS Secrets Manager (mr-mentor-backend/{development,production}, regionap-south-1, profileprojects). Do not print real values in docs, logs, or PRs. docker compose restartdoes not re-readenv_file— useup -dto recreate the container after a Secrets Manager / env change.- Multi-platform behaviour is driven by the
x-platformrequest header and thePlatformentity, not by env switches — the same env serves all tenants. - Reserved flags (
ENABLE_STARTER_BUNDLE,ENABLE_GRANT_AUDIT) are wired but unused; setting them has no effect yet.