Skip to main content
Version: 0.10

Configuration Reference

How configuration is resolved

Environment variables are the primary configuration surface for self-hosted TokenTimer. Defaults come from code fallbacks, then from Compose or Helm example values where applicable.

Some settings can also be stored in the database through the System Settings admin UI. When both exist, the precedence differs per setting:

SettingPrecedence (highest wins)
SMTP configSystem Settings DB > env vars > code defaults
Twilio credentials and template SIDsSystem Settings DB > env vars > code defaults
Alert thresholds, delivery windowWorkspace preferences DB > env vars > code defaults
CERTOPS_ENABLEDEnv var > System Settings DB > default (false)
Admin bootstrapEnv vars only

In the tables below, unset means optional and disabled unless you set it. required means you must provide a value for production.

Core runtime and bootstrap

VariableDescriptionDefault valueScope
TT_MODECore variant modeossApp mode
NODE_ENVRuntime environmentdevelopmentAPI, worker, dashboard
SESSION_SECRETSession signing secretrequiredAPI, worker
ADMIN_EMAILFirst admin bootstrap emailadmin@your-company.comAPI bootstrap
ADMIN_PASSWORDFirst admin bootstrap passwordChangeThisSecurePassword123!API bootstrap
ADMIN_NAMEFirst admin bootstrap display nameAdministratorAPI bootstrap
DISABLE_ADMIN_BOOTSTRAPSkip first admin auto-create when truefalseAPI bootstrap
PORTAPI listen port4000API
HOSTAPI bind host0.0.0.0API
API_PORTCompose API port mapping4000Local compose
DASHBOARD_PORTCompose dashboard port mapping5173Local compose

See Authentication for the full admin bootstrap flow.

Canonical URLs and API docs

VariableDescriptionDefault valueScope
APP_URLFrontend public URL (canonical)http://localhost:5173API, worker, dashboard
API_URLBackend public URL (canonical)http://localhost:4000API, worker, dashboard
ENABLE_API_DOCSEnable Swagger UI (/api-docs) in productionfalseAPI docs serving
VITE_API_URLFrontend build-time API overridehttp://localhost:4000Dashboard build
OPENAPI_SPEC_PATHOptional absolute path to OpenAPI YAMLunset (auto-discovery)API docs serving
PUBLIC_BASE_URLPublic URL used for webhook signature validationunset (falls back to API_URL)API webhook auth

Helm vs Compose vs runtime

LayerAPP_URLAPI_URL when unset in your config
Helm (config.baseUrl / apiUrl)baseUrlapiUrl if set, else baseUrl (same host)
Helm stock values.yamlhttp://localhost:8080http://localhost:4000 (both set explicitly)
Docker Compose (.env)${APP_URL:-http://localhost:5173}${API_URL:-http://localhost:4000} (no cross-fallback in compose files)
API process (env missing)http://localhost:5173API_URL, else APP_URL, else http://localhost:4000
warning

In local http://localhost with NODE_ENV=production, the browser does not persist or send the secure session cookie for the auth flow. Use HTTPS in front of the API and dashboard (reverse proxy with TLS), otherwise secure cookies are expected to fail in local HTTP.

Split-host deployments (dashboard and API on different origins)

When APP_URL and API_URL differ on HTTPS (for example dashboard at https://app.example.com and API at https://api.example.com), the API:

  • Allows both origins in CORS (configured APP_URL and API_URL only in production; localhost dev origins are omitted unless ALLOW_LOCAL_DEV_CORS=true).
  • Sets session and CSRF cookies to SameSite=None; Secure on the API host. Host-only cookies (no SESSION_COOKIE_DOMAIN) are enough for credentialed withCredentials calls from the dashboard to api.example.com. Set SESSION_COOKIE_DOMAIN only when you intentionally need a parent-domain cookie shared across multiple subdomains (broader scope; weaker isolation).

HTTP split-host (including LAN IPs or internal DNS without TLS) does not use SameSite=None; cookies stay Lax because None requires Secure and is often blocked. Use HTTPS for split-host production, or put UI and API behind one origin (ingress).

Docker Compose (http://localhost:5173 + http://localhost:4000) and Helm port-forward (http://localhost:8080 + http://localhost:4000) keep SameSite=Lax (same-site across ports on local HTTP). For plain HTTP + NODE_ENV=production, set SESSION_COOKIE_SECURE_LOCALHOST_OVERRIDE=true only when both APP_URL and API_URL are local HTTP (localhost / 127.0.0.1); the flag is ignored on internet-facing HTTPS URLs.

Same-host installs (single ingress hostname for UI and API) can leave SESSION_COOKIE_DOMAIN unset. Helm defaults often use baseUrl: http://localhost:8080 and apiUrl: http://localhost:4000; Compose maps the dashboard to host port 5173 by default (APP_URL=http://localhost:5173). Always set APP_URL and API_URL to the origins users and integrations actually use in the browser.

Database and pooling

VariableDescriptionDefault valueScope
DB_HOSTPostgreSQL hostlocalhost (code), postgres (compose)API, worker
DB_PORTPostgreSQL port5432API, worker
DB_NAMEPostgreSQL database nametokentimerAPI, worker
DB_USERPostgreSQL usertokentimerAPI, worker
DB_PASSWORDPostgreSQL passwordpassword (dev fallback), required (prod)API, worker
DATABASE_URLFull PostgreSQL connection URL overrideunsetAPI, worker
DB_SSLDB SSL mode (require or verify)unset (disabled)API, worker
PGSSLROOTCERTPath to CA cert for DB SSL verify modeunsetAPI, worker
DB_POOL_MAXMax DB pool connections10API, worker
DB_POOL_MINMin DB pool connections2API, worker
DB_POOL_IDLE_TIMEOUTIdle connection timeout (ms)30000API, worker
DB_CONNECTION_TIMEOUTDB connection timeout (ms)5000API, worker
note

PostgreSQL is the only stateful component. See Backup and restore for backup guidance.

Authentication and security

VariableDescriptionDefault valueScope
LOCAL_AUTH_ENABLEDEnable local email/password authtrueAPI auth
REQUIRE_EMAIL_VERIFICATIONRequire verified email before accesstrueAPI auth
TWO_FACTOR_ENABLEDEnable 2FA featurestrueAPI auth
SESSION_MAX_AGESession max age in ms86400000API auth
SESSION_COOKIE_SECURE_LOCALHOST_OVERRIDEAllow insecure session cookies in production only when APP_URL and API_URL are both local HTTP (localhost / 127.0.0.1); ignored otherwisefalseAPI auth
SESSION_COOKIE_DOMAINOptional parent domain for session/CSRF cookies (e.g. .example.com) when you need cookies shared across subdomains; not required for typical split-host API callsunsetAPI auth
ALLOW_LOCAL_DEV_CORSIn production, also allow http://localhost:* and http://127.0.0.1:* in CORS (local troubleshooting only)falseAPI security
CSRF_ENABLEDEnable CSRF protectiontrueAPI security
MIN_PASSWORD_LENGTHMinimum password length8API auth
REQUIRE_UPPERCASEEnforce uppercase in passwordstrueAPI auth
REQUIRE_NUMBERSEnforce numeric chars in passwordstrueAPI auth
PHONE_HASH_SALTOptional salt for phone hashingunsetAPI privacy
TRUST_PROXY_HOPSNumber of trusted reverse-proxy hops in front of the API (affects req.ip and req.protocol resolution). 0 = no proxy, 1 = single ingress/reverse proxy, 2 = LB -> ingress.2API security
WORKER_API_KEYWorker-to-API auth keyunset (falls back to SESSION_SECRET)Worker, API

Email and delivery (SMTP)

VariableDescriptionDefault valueScope
SMTP_HOSTSMTP host(s) (comma-separated)localhost in config helper, unset in compose examplesAPI, worker email
SMTP_PORTSMTP port(s) (comma-separated)587 in config helperAPI, worker email
SMTP_USERSMTP username(s) (comma-separated)unsetAPI, worker email
SMTP_PASSSMTP password(s) (comma-separated)unsetAPI, worker email
FROM_EMAILSender email overrideunsetAPI, worker email
FROM_EMAIL_NAMESender display nameTokenTimerAPI, worker email
SMTP_SECUREForce SMTPS/SSLfalseAPI, worker email
SMTP_REQUIRE_TLSRequire STARTTLS upgradetrueAPI, worker email
SMTP_REJECT_UNAUTHORIZEDReject invalid TLS certsunsetAPI, worker email
note

SMTP can be configured via env vars or from the System Settings admin UI. Env vars serve as bootstrap values; once an admin configures SMTP in the UI, the database values take precedence.

Alerts, limits, and webhooks

VariableDescriptionDefault valueScope
ALERT_THRESHOLDSDefault days-before-expiry thresholds (overridden by workspace preferences)30,14,7,1,0Alerts
ALERT_MAX_ATTEMPTSMax delivery retries per alert20Alerts
ALERT_RETRY_DELAY_MSRetry delay in ms300000Alerts
ALERT_TEST_UTC_DAYTest-only scheduler day overrideunsetAlerts testing
GLOBAL_RATE_LIMIT_WINDOW_MSGlobal limiter window in ms60000API rate limiting
GLOBAL_RATE_LIMIT_MAXGlobal limiter max requests per window300 (prod) / 1000 (dev,test)API rate limiting
GLOBAL_SLOWDOWN_WINDOW_MSGlobal slowdown window in ms900000API rate limiting
GLOBAL_SLOWDOWN_DELAY_AFTERRequests before delay is applied50API rate limiting
GLOBAL_SLOWDOWN_DELAY_MSAdded delay per request after threshold500API rate limiting
LOGIN_RATE_LIMIT_WINDOW_MSLogin limiter window in ms900000Auth rate limiting
LOGIN_RATE_LIMIT_MAXLogin attempts per window10 (prod) / 500 (dev,test)Auth rate limiting
LOGIN_EMAIL_RATE_LIMIT_MAXLogin attempts per email per window5 (prod) / 500 (dev,test)Auth rate limiting
PASSWORD_RESET_RATE_LIMIT_WINDOW_MSPassword reset limiter window in ms900000Auth rate limiting
PASSWORD_RESET_RATE_LIMIT_MAXPassword reset attempts per window5 (prod) / 100 (dev,test)Auth rate limiting
PASSWORD_RESET_EMAIL_RATE_LIMIT_MAXPassword reset attempts per email per window3 (prod) / 200 (dev,test)Auth rate limiting
EMAIL_VERIFICATION_RATE_LIMIT_WINDOW_MSEmail verification limiter window in ms3600000Auth rate limiting
EMAIL_VERIFICATION_RATE_LIMIT_MAXEmail verification attempts per window20 (prod) / 500 (dev,test)Auth rate limiting
AUTH_SLOWDOWN_WINDOW_MSAuth slowdown window in ms900000Auth rate limiting
AUTH_SLOWDOWN_DELAY_AFTERAuth requests before slowdown5 (prod) / 1000 (dev,test)Auth rate limiting
AUTH_SLOWDOWN_DELAY_MSAuth slowdown delay per request in ms500Auth rate limiting
AUTH_SLOWDOWN_MAX_DELAY_MSAuth slowdown max delay in ms10000Auth rate limiting
API_RATE_LIMIT_WINDOW_MSPlan-aware API limiter window in ms900000API rate limiting
TEST_API_RATE_LIMIT_WINDOW_MSTest/dev API limiter window in ms900000API rate limiting
TEST_API_RATE_LIMIT_MAXTest/dev API limiter max requests1000 (prod) / 10000 (dev,test)API rate limiting
DELIVERY_WINDOW_DEFAULT_STARTDefault delivery window start (UTC, overridden by workspace preferences)00:00Alerts
DELIVERY_WINDOW_DEFAULT_ENDDefault delivery window end (UTC, overridden by workspace preferences)23:59Alerts
DELIVERY_WINDOW_DEFAULT_TZDefault delivery timezone (overridden by workspace preferences)UTCAlerts
DELIVERY_WINDOW_DEFERRAL_MSDefer delay when outside windowunsetAlerts
MAX_WEBHOOKSMax webhooks per workspaceunsetAlerts/webhooks
TEST_WEBHOOK_RATE_LIMIT_1M_WINDOW_MSTest webhook short limiter window in ms60000Alerts/webhooks
TEST_WEBHOOK_RATE_LIMIT_1M_MAXTest webhook max requests in short window5Alerts/webhooks
TEST_WEBHOOK_RATE_LIMIT_5M_WINDOW_MSTest webhook long limiter window in ms300000Alerts/webhooks
TEST_WEBHOOK_RATE_LIMIT_5M_MAXTest webhook max requests in long window10Alerts/webhooks
TEST_WEBHOOK_COOLDOWN_MSPer-user test webhook cooldown after each attempt5000Alerts/webhooks
WEBHOOK_ALLOW_ALL_HOSTSAllow all webhook destinations when truefalseWebhook security
WEBHOOK_PROVIDER_HOSTSExtra allowed provider hostsempty (built-in list still allowed)Webhook security
WEBHOOK_EXTRA_PROVIDER_HOSTSAdditional allowed hostsemptyWebhook security
VAULT_ADDRESS_ALLOWLISTVault integration host allowlistempty (no host restriction)Integration security
AZURE_VAULT_ADDRESS_ALLOWLISTAzure Key Vault host allowlistempty (no host restriction)Integration security
GITHUB_ADDRESS_ALLOWLISTGitHub host allowlistempty (no host restriction)Integration security
GITLAB_ADDRESS_ALLOWLISTGitLab host allowlistempty (no host restriction)Integration security
INTEGRATION_SCAN_LIMITSJSON plan-to-limit map (core defaults unlimited){"oss":Infinity}Integration quotas
DOMAIN_CHECKER_DISCOVERY_LIMITSDomain checker discovery result cap map (plan:value)oss:10000000Domain checker
DOMAIN_CHECKER_IMPORT_LIMITSDomain checker import request cap map (plan:value)oss:50000Domain checker
DOMAIN_CHECKER_MAX_RESULTSDirect override for discovery results, capped internally at 25,000,000unsetDomain checker
DOMAIN_CHECKER_IMPORT_MAX_CERTIFICATESDirect override for import certificates per request, capped at 200,000unsetDomain checker
CONTACT_GROUP_LIMITSJSON plan-to-limit map (core defaults unlimited){"oss":Infinity}Contact groups
CONTACT_GROUP_MEMBER_LIMITSJSON plan-to-limit map (core defaults unlimited){"oss":Infinity}Contact groups
WORKSPACE_PLAN_LIMITSJSON plan-to-limit map (core defaults unlimited){"oss":Infinity}Workspaces
MEMBER_PLAN_LIMITSJSON plan-to-limit map (core defaults unlimited){"oss":Infinity}Workspace members

CertOps (certificate operations)

VariableDescriptionDefault valueScope
CERTOPS_ENABLEDEnable the certificate operations layer. When enabled, TokenTimer maintains a managed-certificate inventory linked to cert-category tokens, accepts public certificate (PEM) import, and bridges observations from HTTPS endpoint/domain monitors into the inventory (when the monitor has a linked token). When disabled, CertOps API endpoints return 404.falseCertOps

TokenTimer stores only public certificate material (fingerprints, serials, issuers, subjects, SANs, validity, chains) and external key references. Requests containing private key material are rejected with HTTP 422.

For the flag resolution order and rollout guidance, see Enable CertOps.

Worker scheduling

TokenTimer runs five background workers:

  • Alert discovery scans tokens and queues expiry alerts.
  • Alert delivery processes the alert queue and sends notifications (email, webhooks, WhatsApp).
  • Auto-sync runs scheduled integration scans (for example Vault or GitHub imports).
  • Endpoint check monitors HTTPS endpoints and their SSL certificates.
  • Weekly digest sends the weekly token-expiry summary.

How they are scheduled depends on the deployment method:

  • Docker Compose: each worker runs as its own long-running container (worker-discovery, worker-delivery, worker-auto-sync, worker-endpoint-check, worker-weekly-digest) using the built-in runner. The runner schedules jobs in-process from a cron expression; the container restart: unless-stopped policy is crash recovery only, not the scheduler.
  • Helm (Kubernetes): each worker is a Kubernetes CronJob that starts a one-shot process per scheduled run. Schedules come from worker.cronjobs.<name>.schedule in values.yaml, not from the WORKER_*_CRON variables below. The chart defaults match the runner defaults.

A cron expression is a five-field schedule string (minute hour day-of-month month day-of-week), for example */5 * * * * for every 5 minutes.

VariableDescriptionDefault valueScope
WORKER_DISCOVERY_CRONAlert discovery schedule (scan tokens, queue expiry alerts)*/5 * * * *Worker (Compose)
WORKER_DELIVERY_CRONAlert delivery schedule (send queued notifications), offset 1 min after discovery1/5 * * * *Worker (Compose)
WORKER_AUTO_SYNC_CRONAuto-sync schedule (scheduled integration scans)*/1 * * * *Worker (Compose)
WORKER_ENDPOINT_CHECK_CRONEndpoint/SSL check schedule*/1 * * * *Worker (Compose)
WORKER_WEEKLY_DIGEST_CRONWeekly digest schedule (Monday 09:00)0 9 * * 1Worker (Compose)

Cron expressions are evaluated in the container timezone. Compose worker services set TZ: ${TZ:-UTC}, so set TZ in deploy/compose/.env (for example TZ=Europe/Zurich) to shift schedules to local time.

Run on start

In cron mode, workers wait for their next scheduled run and do not run immediately at process start. Set a run-on-start variable to true to also run the job once at startup.

VariableDescriptionDefault valueScope
WORKER_RUN_ON_STARTGlobal run-on-start for discovery, delivery, auto-sync, and endpoint check. Not inherited by weekly digest.falseWorker
WORKER_DISCOVERY_RUN_ON_STARTPer-worker override for alert discoveryfalseWorker
WORKER_DELIVERY_RUN_ON_STARTPer-worker override for alert deliveryfalseWorker
WORKER_AUTO_SYNC_RUN_ON_STARTPer-worker override for auto-syncfalse (code), true (compose)Worker
WORKER_ENDPOINT_CHECK_RUN_ON_STARTPer-worker override for endpoint checkfalseWorker
WORKER_WEEKLY_DIGEST_RUN_ON_STARTRun the weekly digest immediately at startup. Required for weekly digest; it ignores the global variable.falseWorker

The Compose worker-auto-sync service sets WORKER_AUTO_SYNC_RUN_ON_START=true by default, so an integration scan runs when that container starts.

In interval mode, discovery, delivery, auto-sync, and endpoint check run once at startup by default (the weekly digest still does not).

Supported cron syntax

The runner accepts standard five-field cron expressions with:

  • * for any value
  • step values: */n and n/m (start at n, repeat every m)
  • exact integers, ranges (n-m), and comma-separated lists
  • day-of-week 0-7, where both 0 and 7 mean Sunday

Not supported: named months or weekdays (JAN, MON), and the L, W, and # modifiers. Invalid expressions fail at worker startup with an error naming the variable.

Interval mode

Cron mode is the default. To use fixed millisecond intervals instead, set the worker's cron variable to interval (or 0, false, off) and set the matching interval variable:

WORKER_DISCOVERY_CRON=interval
WORKER_DISCOVERY_INTERVAL_MS=60000

WORKER_*_INTERVAL_MS variables are ignored while the worker is in cron mode.

VariableDescriptionDefault valueScope
WORKER_DISCOVERY_INTERVAL_MSDiscovery interval (ms)60000Worker
WORKER_DELIVERY_INTERVAL_MSDelivery interval (ms)30000Worker
WORKER_AUTO_SYNC_INTERVAL_MSAuto-sync interval (ms)300000Worker
WORKER_ENDPOINT_CHECK_INTERVAL_MSEndpoint check interval (ms)60000Worker
WORKER_WEEKLY_DIGEST_INTERVAL_MSWeekly digest interval (ms)86400000Worker

Failure behavior

VariableDescriptionDefault valueScope
WORKER_EXIT_ON_ERRORWhen false, job failures are logged (worker-runner-job-failure) and the runner keeps running. When true, the process exits on the first unhandled job error and the container restart policy recovers it.falseWorker

For worker health metrics (runner_up, delivery and digest gauges), see Monitoring and metrics.

Metrics and observability

VariableDescriptionDefault valueScope
ENABLE_METRICSEnable /metrics and metric push attempts when truefalseAPI, worker
PUSHGATEWAY_URLPrometheus Pushgateway endpointunsetWorker metrics
ENVIRONMENT_SUFFIXMetrics environment label (if you have multiple instances)unsetWorker metrics

WhatsApp (Twilio)

VariableDescriptionDefault valueScope
TWILIO_ACCOUNT_SIDTwilio account SIDunsetWhatsApp
TWILIO_AUTH_TOKENTwilio auth tokenunsetWhatsApp
TWILIO_WHATSAPP_FROMWhatsApp sender (E.164)unsetWhatsApp
TWILIO_WHATSAPP_ALERT_CONTENT_SID_EXPIRESContent template SID for expiring alertsunsetWhatsApp
TWILIO_WHATSAPP_ALERT_CONTENT_SID_EXPIREDContent template SID for expired alertsunsetWhatsApp
TWILIO_WHATSAPP_ALERT_CONTENT_SID_ENDPOINT_DOWNContent template SID for endpoint down alertsunsetWhatsApp
TWILIO_WHATSAPP_ALERT_CONTENT_SID_ENDPOINT_RECOVEREDContent template SID for endpoint recovered alertsunsetWhatsApp
TWILIO_WHATSAPP_WEEKLY_DIGEST_CONTENT_SIDContent template SID for weekly digestunsetWhatsApp
TWILIO_WHATSAPP_TEST_CONTENT_SIDContent template SID for test messagesunsetWhatsApp
WHATSAPP_RATE_PER_MINOutbound WhatsApp message rate capunsetWhatsApp throttling
WHATSAPP_DRY_RUNLog messages without sending when truefalseWhatsApp testing

Admin setup: Twilio WhatsApp

Use this flow when you want to enable WhatsApp notifications, expiry templates, weekly digest, and endpoint alert templates.

1. Configure Twilio credentials in System Settings. As an admin, open System Settings > WhatsApp (Twilio) and fill in the Account SID, Auth Token, and WhatsApp From Number. Optional template SIDs configurable in the UI: Alert (Expires), Alert (Expired), Test Message, and Weekly Digest.

2. Configure endpoint alert template SIDs at deployment level. Endpoint down and endpoint recovered WhatsApp alerts use dedicated deployment variables:

  • TWILIO_WHATSAPP_ALERT_CONTENT_SID_ENDPOINT_DOWN
  • TWILIO_WHATSAPP_ALERT_CONTENT_SID_ENDPOINT_RECOVERED

Set them in your deployment method: deploy/compose/.env for Compose, deploy/helm/values.yaml for Helm, or your own secret/config injection. After changing these values, restart the delivery worker so it picks up the new template SIDs.

3. Create Twilio content templates. Create two approved Twilio WhatsApp templates for endpoint down and endpoint recovered. The exact template bodies, placeholder contracts, and copy-ready examples for all TokenTimer templates are documented in Contact groups & channels → WhatsApp templates setup.

Variables sent by TokenTimer for endpoint WhatsApp alerts: recipient_name, endpoint_name, endpoint_url, token_name, detected_at.

4. Verify.

  • Use Send test WhatsApp in System Settings to verify base Twilio credentials and sender configuration.
  • Trigger an endpoint outage and confirm the delivery log shows channel whatsapp.
  • Confirm the delivery metadata records template_kind = endpoint_down for outage alerts and template_kind = endpoint_recovered for recovery alerts.

Legacy and compatibility

VariableDescriptionDefault valueScope
PLAN_API_LIMITSAPI limiter plan map override (parsed as plan:value, fallback oss:6000)unsetAPI middleware
RATE_LIMIT_WINDOWConfig package generic rate-limit window (ms)60000Shared config consumers
RATE_LIMIT_MAXConfig package generic rate-limit max requests100Shared config consumers