Secrets enter the application in exactly one place, and every value below is read
there and validated. Nothing else in the codebase reads the environment directly.
Variable
Needed by
Behaviour if missing
GOOGLE_GENERATIVE_AI_API_KEY
everything that talks to a model: evaluation, embeddings, drafting
Startup fails. Accepts the plural form instead (below)
MongoDB_URI
run-due, serve, all watches:* and users:* commands
Fails fast with an actionable message
AUTH_JWT_SECRET
pnpm serve only
The API refuses to start. Must be 32+ characters
# generate a suitable secret
node -e " console.log(require('crypto').randomBytes(32).toString('base64url')) "
Variable
Default
What it does
GOOGLE_GENERATIVE_AI_API_KEYS
—
Comma-separated pool of Google keys. Takes precedence over the singular form
GROQ_API_KEY
—
Second-opinion model. Absent → Watcher runs primary-only
GROQ_API_KEYS
—
Comma-separated pool for the second opinion
GROQ_MODEL
provider default
Overrides the second-opinion model, when no stored category config exists
CONFIG_ENCRYPTION_KEY
—
Master key for the encrypted key store. Absent → stored keys are ignored with a warning and environment keys are used
Variable
Default
What it does
TAVILY_API_KEY
—
Required for search sources; also enables the preferred page-extraction tier. Absent → search fails at collect time, pages fall back to plain fetching
JINA_API_KEY
—
Optional. The reader tier works without a key; a key raises its limits
Variable
Default
What it does
telegram_bot_token
—
Telegram bot token. Absent → the telegram channel logs to the console
telegram_chat_id
—
Default destination chat
TELEGRAM_ADMIN_CHAT_ID
falls back to telegram_chat_id
Where ⚠️ health alerts go
FIREBASE_SERVICE_ACCOUNT_JSON
—
Service-account JSON for Android push. Absent → the push channel logs to the console. Malformed → startup fails
Variable
Default
What it does
PORT
3000
Listen port
SCHEDULER_ENABLED
true
Set to exactly false to disable the in-process scheduler
SCHEDULER_CRON
*/5 * * * *
Scheduler tick, in UTC. Invalid → startup fails
RUN_CONCURRENCY
1
How many watches a pass processes at once. Invalid values fall back to 1
WEB_ORIGIN
—
Comma-separated browser origins allowed to call the API. Absent → no CORS headers at all (same-origin and non-browser callers only). Never a wildcard
Variable
Default
What it does
AUTH_TOKEN_TTL
604800 (7 days)
Access-token lifetime in seconds. Invalid → the default
ADMIN_PASSWORD
—
Password for pnpm users:create, so it stays out of shell history
GOOGLE_OAUTH_CLIENT_IDS
—
Comma-separated accepted Google OAuth client ids. Absent → Google sign-in is disabled
SMTP_HOST
—
Absent → verification and reset links are logged to the console instead of sent
SMTP_PORT
587
SMTP_SECURE
false (true when the port is 465)
SMTP_USER, SMTP_PASSWORD
—
Sent only when both are present
SMTP_FROM
falls back to SMTP_USER
From address
APP_URL
falls back to the first WEB_ORIGIN
Base URL used in emailed links
Variable
Used by
MONGO_TEST_URI, MONGO_TEST_DB
The integration test; it self-skips when unset
EVAL_LIVE_DELAY_MS
Pacing for the live accuracy harness (default 7000)
curl -s https://<your-api>/config/status
Returns booleans only — never a value. It is public, so it does reveal which
integrations an instance has configured.
Handling these values
Keep them in .env (git-ignored) or your platform’s secret store. Never commit
them, never paste them into an issue, and never put a real value in a screenshot.
Watcher itself never logs a secret and never returns one over the API.