# ───────────────────────────────────────────────────────────────────────────── # Field Service Work Order Management — environment template # # Copy to `.env` and fill in real values: # cp .env.example .env # NEVER commit `.env`. Real secrets must never be committed to the repo. # ───────────────────────────────────────────────────────────────────────────── # ── HTTP / runtime ──────────────────────────────────────────────────────────── # Port the Express API listens on (default 4000). PORT=4000 # development | test | production NODE_ENV=development # ── Database ────────────────────────────────────────────────────────────────── # MongoDB connection string (required). MONGODB_URI=mongodb://127.0.0.1:27017/workorders # ── Secrets (REQUIRED) ──────────────────────────────────────────────────────── # HS256 signing secret for the 15-minute access JWT. # MUST be >= 32 characters and MUST NOT be a placeholder such as "secret", # "changeme", or "replace-me". Rotating it invalidates issued access tokens. JWT_SECRET=0123456789abcdef0123456789abcdef0123456789abcdef # Secret used by cookie-parser to sign cookie values (integrity only). # MUST be >= 32 characters. It is NOT authentication. COOKIE_SECRET=fedcba9876543210fedcba9876543210fedcba9876543210 # NOTE: JWT_SECRET and COOKIE_SECRET must each be at least 32 characters and # must not be placeholder values. Generate real ones with: # openssl rand -hex 32 # and never commit them to version control. # ── CORS / URLs ─────────────────────────────────────────────────────────────── # Comma-separated list of allowed origins. `credentials: true` is set, so "*" # is rejected. Same-origin deployments only need the SPA origin. CORS_ORIGIN=http://localhost:5173 # Public SPA origin (used for password-reset links and cookie handling). APP_URL=http://localhost:5173 # ── Mailer ──────────────────────────────────────────────────────────────────── # Resend API key (https://resend.com/docs/api-reference/emails/send). # Leave empty to use the log-only mailer (logs a REDACTED reset URL). RESEND_API_KEY= # Verified sender. Defaults to Resend's shared onboarding@resend.dev. # Use an address on your verified domain (e.g. no-reply@mail.lverma.com). RESEND_FROM=Work Order Desk # ── Proxy / observability ───────────────────────────────────────────────────── # Number of trusted reverse-proxy hops. nginx sets X-Forwarded-* in prod; # keep 0 when the API is reached directly (local dev). TRUST_PROXY_HOPS=0 # Set to true to return stack traces in 500 responses (dev only, never prod). DEBUG_ERRORS=false # pino log level: trace | debug | info | warn | error | fatal LOG_LEVEL=info # ── Rate limiting (window in ms, max requests per key) ──────────────────────── RATE_LIMIT_WINDOW_MS=60000 RATE_LIMIT_MAX=300 RATE_LIMIT_LOGIN_MAX=10 RATE_LIMIT_FORGOT_MAX=3 # ── Seed credentials (`npm run seed`, idempotent) ───────────────────────────── SEED_ADMIN_EMAIL=admin@example.com SEED_ADMIN_PASSWORD=Admin1234 SEED_USER_EMAIL=user@example.com SEED_USER_PASSWORD=User1234 SEED_VIEWER_EMAIL=viewer@example.com SEED_VIEWER_PASSWORD=Viewer1234 # ── Frontend (Vite, consumed at build time) ─────────────────────────────────── # Must match APP_URL. VITE_APP_URL=http://localhost:5173 # Empty in dev → relative `/api/v1`, proxied by Vite (dev) or nginx (prod). # Set to the API origin only for a split-origin deployment. VITE_API_URL= # ── Agentic AI ──────────────────────────────────────────────────────────────── # OPT-IN. Off by default: a fresh checkout without these keys boots normally # and the AI surfaces (copilot, triage worker) are inert. Setting # AI_ENABLED=true REQUIRES a real AI_BASE_URL (https, non-private host) and # AI_API_KEY. AI_API_KEY is a server-side secret — NEVER commit it, never send # it to the browser. The backend reads these keys via backend/src/config/env.ts # (from .env at the repo root or backend/); the worker process reads the same # .env and needs no extra configuration. # Master switch. false = copilot API and triage worker refuse to run (hard kill). AI_ENABLED=false # OpenAI-compatible chat/completions base URL (e.g. https://api.openai.com/v1 # or a compatible gateway). Must be https and NOT a private/loopback host. AI_BASE_URL= # Server-side API key for the provider. Never committed; pino redacts it. AI_API_KEY= # Model name passed to the provider (default gpt-4o-mini). AI_MODEL=gpt-4o-mini # Max tool-call steps per agent run, incl. retries and failed parses (default 8). AI_MAX_STEPS_PER_RUN=8 # Max tokens a single provider call may output (default 2048). AI_MAX_OUTPUT_TOKENS=2048 # Per-run prompt (context) token budget (default 16384). AI_MAX_CONTEXT_TOKENS=16384 # Cost per 1M tokens used for spend accounting (USD, defaults 0.15 / 0.60). AI_PRICE_PER_1M_INPUT=0.15 AI_PRICE_PER_1M_OUTPUT=0.60 # Daily spend caps in USD (defaults 1.00 per user / 1.00 per agent / 5.00 global). AI_DAILY_SPEND_USD=1.00 AGENT_DAILY_SPEND_USD=1.00 AI_GLOBAL_DAILY_SPEND_USD=5.00 # Copilot approval lifetime in ms before a staged write expires (default 600000). AI_APPROVAL_TTL_MS=600000 # SSE keepalive interval in ms (default 15000). AI_SSE_KEEPALIVE_MS=15000 # AI-specific rate limit: stream starts per (user + IP) window (default 20). AI_RATE_LIMIT_MAX=20 # Triage worker poll interval in ms (default 5000). AGENT_POLL_INTERVAL_MS=5000 # Outbox event claim lease in ms; a crashed claim is requeued after this (default 15000). AGENT_LEASE_MS=15000 # Worker sweep interval in ms (stale runs, expired approvals, reconcile) (default 30000). AGENT_SWEEP_INTERVAL_MS=30000 # Max attempts per outbox event before it is marked failed (default 3). AGENT_MAX_ATTEMPTS=3 # Reconcile pass re-emits events older than this (default 60000). AGENT_RECONCILE_AFTER_MS=60000 # Triage mode: suggest | auto-apply (default suggest). AGENT_TRIAGE_MODE=suggest # Working-hours window for autonomous triage; "*" = 24/7 (default *). AGENT_WORKING_HOURS=* # Max concurrent outbox claims per worker (default 2). AGENT_CONCURRENCY=2