From 54c5351e009cad6bb059558f4cbc3e3ca6e4feec Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Mon, 18 May 2026 00:39:19 -0500 Subject: [PATCH] docs(env): document the 9 runtime env vars missing from .env.example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `.env.example` had drifted: the runtime reads 17 env vars but only documented 8 of them. New entries cover: - LOG_LEVEL, LOG_PRETTY — pino logger knobs (existed since the initial port; never documented). - RATE_LIMIT_MAX, RATE_LIMIT_WINDOW_MS — express-rate-limit tuning (existed since the brute-force defense PR). - JSON_BODY_LIMIT — body size cap (existed since #46). - HELMET_CSP — opt-in CSP toggle (existed since the helmet PR). - TRUST_PROXY — reverse-proxy XFF trust (existed since the proxy PR). - METRICS_BEARER_TOKEN — new in P4-J. Optional bearer-token gate on /metrics. - SHUTDOWN_TIMEOUT_MS — graceful-shutdown drain budget. All commented-out (leading `#`) with the production-default in the comment so a fresh `cp .env.example .env` doesn't accidentally change behavior; operators uncomment + edit only what they need. No code changes; documentation only. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.env.example b/.env.example index 4998052..81ce83d 100644 --- a/.env.example +++ b/.env.example @@ -50,3 +50,57 @@ DB_USER=timetracker # Set a real password here. The configured database user must have access # to the `dbo` schema. Do NOT commit a populated `.env`. DB_PASSWORD=changeme + +# ---- Logging ---- + +# pino log level. One of: trace|debug|info|warn|error|fatal|silent. +# Default 'info' is fine for production; turn up to 'debug' when chasing +# a bug, down to 'warn' if log volume is a budget concern. +# LOG_LEVEL=info + +# Set to '1' to switch the JSON output to a human-readable pretty +# format via pino-pretty. Useful in development; leave unset in +# production so log aggregators get the structured JSON they expect. +# LOG_PRETTY= + +# ---- Rate limiting ---- + +# Per-key request budget for /v1/* in the window below. Defaults to 100. +# Set to 0 to disable rate limiting entirely (useful for load tests). +# RATE_LIMIT_MAX=100 + +# Rolling window in milliseconds. Defaults to 15 minutes (900000). +# RATE_LIMIT_WINDOW_MS=900000 + +# ---- Body size + headers ---- + +# Maximum request body size (express.json limit). Defaults to '100kb'. +# Accepts the same forms as bytes (e.g. '512kb', '1mb'). Bumping this +# is rarely needed — the largest legitimate body in the schema is a +# TimeEntry create with a 10000-char teDescription. +# JSON_BODY_LIMIT=100kb + +# Set to '1' to re-enable helmet's Content-Security-Policy. Disabled by +# default because this is a JSON API and a misconfigured CSP would +# break Swagger UI at /docs. +# HELMET_CSP= + +# ---- Reverse-proxy / observability ---- + +# When set, the server trusts X-Forwarded-* headers from a reverse +# proxy so rate-limit / log-IP / etc. resolve to the real client IP. +# Accepts 'true' (trust any proxy) or a hop count (e.g. '1' for one +# reverse proxy in front). Default false — never trust XFF from a +# non-proxied client. +# TRUST_PROXY=true + +# Optional bearer token gating /metrics. Unset = open scrape (the +# usual private-network deployment pattern). When set, the +# Prometheus scrape must include `Authorization: Bearer `. +# METRICS_BEARER_TOKEN= + +# How long the graceful-shutdown drain may run before we force-exit +# with code 1. Default 25_000 (25 seconds) — long enough for in-flight +# requests to finish, short enough that systemd / k8s don't SIGKILL +# us first. +# SHUTDOWN_TIMEOUT_MS=25000