Skip to content

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 every process.env.* reference under src/, and reading src/utils/featureFlags.ts, src/services/SystemConfigService.ts, src/entities/SystemConfig.ts, src/config/database.ts and src/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.tsgetAll / 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 /:key so 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.ts hard-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: LeegalitySandboxService reads env first, then falls back to the encrypted mas101_leegality_sandbox_* rows in system_configs (decrypted with getEncryptionKey()).

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 URLLeegalitySandboxService tries process.env.LEEGALITY_SANDBOX_BASE_URL, else decrypts mas101_leegality_sandbox_base_url from system_configs. - Auto-screen on applymrHire.controller reads runtime global_auto_screen_on_apply; no env equivalent. - DB connectiondatabase.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-var skill: it propagates the var to local .env, the envs/ 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 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=false means 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 to localhost/shubham/shubham/template — a misconfigured prod could connect somewhere unexpected rather than failing loudly.
  • /api/system-config/:key is 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_PREFIX is a no-op — the code reads GLOBAL_INVOICE_MAS101_PREFIX. Easy to set the wrong one.
  • EXOTEL_SUBDOMAIN must match the account cluster (SG vs Mumbai) or every call/SMS 401s.
  • Secrets must never be committed. .env.example ships placeholders only. Real values live in AWS Secrets Manager (mr-mentor-backend/{development,production}, region ap-south-1, profile projects). Do not print real values in docs, logs, or PRs.
  • docker compose restart does not re-read env_file — use up -d to recreate the container after a Secrets Manager / env change.
  • Multi-platform behaviour is driven by the x-platform request header and the Platform entity, 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.