Environment Variables & Config Guide
How environment variables are managed in the Oktivo backend.
How It Works
Environment variables are loaded from AWS Secrets Manager at startup (or from local env/ files when USE_LOCAL_ENV=true). The values are injected into process.env / os.environ before any config module reads them.
env/shared.env + env/{service}.env (local, USE_LOCAL_ENV=true)
OR —
AWS Secrets Manager (USE_LOCAL_ENV=false)
↓
secrets loader injects into process.env / os.environ
↓
src/config/index.ts (TypeScript) / config.py (Python)
↓
controllers / services / etc.Key Behavior
USE_LOCAL_ENV=true→ loads fromenv/shared.env+env/{service}.env(default in docker-compose.dev.yml)USE_LOCAL_ENV=false→ fetches from AWS Secrets Manager- Docker-compose
environment:overrides always win (setdefault / !process.env[key]) - Secrets are fetched once at startup and cached for the process lifetime
Rules
- Never use
process.envin controllers, services, or models. Always import fromconfig. - Add new env vars to
config/index.tsso they're typed, trimmed, and have defaults. - Add new env vars to the matching
env/*.env.examplefile with a comment explaining what it does. - Never put inline comments on the same line as a value. Dotenv does NOT strip them.
- Never put spaces around
=in.envfiles.
.env Syntax
bash
# ✅ Correct
MY_KEY=some_value
# ✅ Comment on its own line is fine
# This key does X
MY_KEY=some_value
# ❌ WRONG inline comment becomes part of the value
MY_KEY=some_value # this is a comment
# MY_KEY will be: "some_value # this is a comment"
# ❌ WRONG spaces around = can cause issues
MY_KEY = some_valueAdding a New Environment Variable
Step 1: Add to AWS Secrets Manager (dev environment)
Add the key-value pair to the appropriate secret in the AWS Console:
- Shared across services →
oktivo/dev/shared - Client backend only →
oktivo/dev/client-backend - Agent only →
oktivo/dev/agent - Analysis only →
oktivo/dev/analysis
Step 2: Add to local env file AND example file
bash
# env/shared.env (real value gitignored)
MY_NEW_KEY=actual_value
# env/shared.env.example (placeholder committed to git)
# Description of what this key does
MY_NEW_KEY=Step 3: Add to src/config/index.ts (client_backend)
typescript
export const config = {
// ... existing config ...
// Add under the relevant section, with .trim() and a sensible default
myNewKey: process.env.MY_NEW_KEY?.trim() || "default_value",
} as const;Step 4: Use in your code
typescript
import { config } from "../config/index.js";
// ✅ Correct
const value = config.myNewKey;
// ❌ Wrong never do this
const value = process.env.MY_NEW_KEY;Step 5: Add to stage/prod secrets before merging
The secrets-sync-check CI workflow will block your PR if the key is missing in the target environment. Add it to oktivo/stage/* before merging to stage, and oktivo/prod/* before merging to main.
Config Sections
| Section | Env Vars | Purpose |
|---|---|---|
| Core | PORT, MONGO_URI, DB_NAME, NODE_ENV | Server basics |
| Auth | ACCESS_TOKEN_SECRET, REFRESH_TOKEN_SECRET, *_EXPIRY | JWT tokens |
| LiveKit | LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET | Voice infrastructure |
| AWS S3 | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_RECORDING_BUCKET | Call recordings |
| Redis | REDIS_URL | BullMQ queues, campaign state |
EMAIL_PROVIDER, EMAIL_FROM, RESEND_API_KEY, SES_REGION | Email service | |
| SuperAdmin | SUPERADMIN_EMAIL, SUPERADMIN_API_SECRET | Platform admin |
Trimming
All email config values are .trim()'d in config/index.ts to prevent whitespace issues from .env files. If you add a new env var that could have accidental whitespace (API keys, URLs), add .trim():
typescript
myApiKey: process.env.MY_API_KEY?.trim() || "",