collabSupport/.env.example
jmcqueen 90a56c4640 Fix Prisma overlay tunnel discovery with site-scoped query_params.
Use topology/links and anynetlinks filters with eq/in operators so
store WAN follow-ups return real peer tunnels instead of unscoped dumps.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 09:01:40 -04:00

562 lines
28 KiB
Text

# =============================================================================
# CollabFinder / CollabSupport Environment Variables
# Copy this file to .env and fill in your values.
# =============================================================================
# -----------------------------------------------------------------------------
# Server
# -----------------------------------------------------------------------------
SERVER_PORT=1800
# Logging level: info (default - clean), debug (verbose, includes per-fetch details)
LOG_LEVEL=info
# IANA timezone for "Last checked" footers in chat output (avstatus,
# phonestatus, etc.). The bot often runs in UTC inside Docker; this
# keeps timestamps in operator-local time. Default: America/New_York.
# DISPLAY_TIMEZONE=America/New_York
# Verbose Webex framework debug logs. Default off; auto-enabled when LOG_LEVEL=debug.
# WEBEX_FRAMEWORK_DEBUG=false
# -----------------------------------------------------------------------------
# HTTP API authentication
# -----------------------------------------------------------------------------
# Shared secret required to call destructive /:command HTTP endpoints such as
# /offboarduser, /provision-dect, /provision-vc, /vcmonitor, /calltest, /bulkavstatuscsv,
# /bulkavswitchcsv, /devicesbymodel. Without it, those endpoints fail-closed
# with HTTP 503 — set this to any high-entropy string (e.g. `openssl rand -hex 32`).
# Callers send the token as `Authorization: Bearer <token>` or `X-API-Token: <token>`.
HTTP_API_TOKEN=
# Optional. When set to "true" / "1" / "yes", the same token is also required
# for read-only endpoints (/avstatus, /phonestatus, /av/devices/build/…,
# /phone/devices/build/…, /api/av/*, etc.). Default = false (those endpoints
# stay open so the bundled dashboards keep working without auth headers).
HTTP_API_REQUIRE_AUTH=false
# Optional. Base URL for scripts that call the bot HTTP API (e.g.
# scripts/fetchDectStatusXml.js). Default: http://localhost:${SERVER_PORT}
# HTTP_API_BASE_URL=https://your-bot-host.example.com/CollabSupport
# -----------------------------------------------------------------------------
# Webex Bot (required)
# -----------------------------------------------------------------------------
# Bot token from developer.webex.com. The framework connects to Webex over
# websockets using this token, so no public ingress / webhook URL is needed.
WEBEX_BOT_TOKEN=your-bot-token-here
# Service App credentials (used by WebexServiceAppAuth for backend Webex API
# calls — people lookup, room operations, etc.). Separate from the bot token.
#
# Required scopes (set when creating the service app at developer.webex.com):
# - spark-admin:people_read (people lookup, voicediag reads,
# webexhost, findEmptyLocations)
# - spark-admin:people_write (webexhost: assign licenses,
# voicediag: apply remediations —
# disable DND / clear forwarding /
# disable intercept / enable call
# waiting via /v1/people/{id}/features/*)
# - spark-admin:licenses_read (webexhost, findEmptyLocations)
# - identity:tokens_read (offboarduser: list a user's authorizations)
# - identity:tokens_write (offboarduser: revoke a user's authorizations)
# - spark-admin:calling_cdr_read (/callreport + /calltest CDR via cdr_feed)
# - analytics:read_all (/callreport Webex Reports API — Pro Pack)
# The authorizing admin must also hold Full / User / Device Admin role for the
# token-management calls to succeed.
#
# NOTE — early drafts of /voicediag documented `spark-admin:telephony_config_*`
# scopes and a `/v1/telephony/config/people/{id}/callSettings/*` URL scheme.
# That URL family returns 404 "no static resource" from the Webex gateway.
# The live admin surface is `/v1/people/{id}/features/{feature}` (see
# services/voiceDiag/ for details) and it uses the same `spark-admin:people_*`
# scopes that /webexhost and /offboarduser already require — no additional
# scope work is needed to enable /voicediag.
#
# Refresh tokens preserve their original scope set, so if you ever DO add a
# new scope you must:
# 1. Save the updated scopes on the service app.
# 2. Re-authorize the app for the org as a Full / User admin.
# 3. Delete/rotate tokens/webex-service-tokens.json so the next request
# re-bootstraps with the enlarged scope set.
WEBEX_CLIENT_ID=your-service-app-client-id
WEBEX_CLIENT_SECRET=your-service-app-client-secret
# Path to the rotating service app tokens file (must be writable).
# IMPORTANT: Use a *relative* path (e.g. ./config/...). The same .env works for both:
# - Local runs (resolved against your project root cwd)
# - Docker (resolved against /app inside container; see docker-compose volume mount)
# Do NOT use an absolute host path here — it will break inside the container.
WEBEX_TOKENS_PATH=./config/webex-service-tokens.json
# Optional override for the Webex API base URL (default https://webexapis.com/v1).
# WEBEX_BASE_URL=https://webexapis.com/v1
# CDR feed (/cdr_feed) uses analytics-calling.webexapis.com — a different host than
# webexapis.com. In Docker, HTTP(S)_PROXY may route analytics subdomains through a
# broken proxy while webexapis.com is in NO_PROXY. CDR requests bypass proxy by default.
# CDR feed rate limit queue (cdr_feed allows ~1 request/minute per token)
# CDR_FEED_COOLDOWN_MS=65000
# WEBEX_CDR_ANALYTICS_BASES=https://analytics-calling.webexapis.com/v1,https://analytics.webexapis.com/v1
# -----------------------------------------------------------------------------
# Palo Alto Prisma SD-WAN (formerly CloudGenix) — WAN metrics for /voicediag
# and /phonestatus follow-up
# -----------------------------------------------------------------------------
# The Prisma integration is optional: leave it unconfigured and the WAN
# check bucket in /voicediag skips cleanly ("not applicable — missing
# sdwanSite") and /phonestatus omits the WAN follow-up message. Enable it
# by setting PRISMA_AUTH_MODE + the matching credential block below.
#
# AE convention (encoded in integrations/paloalto/sites.js): Prisma site
# names are `CG${storeNum.padStart(5, '0')}` — store 782 → CG00782, store
# 2477 → CG02477. Exact match. No fuzzy searching.
#
# --- Auth mode selection ---
# PRISMA_AUTH_MODE=sase (default, recommended for production)
# PRISMA_AUTH_MODE=legacy (only if you inherited a CloudGenix service
# account and can't provision a SASE one)
# PRISMA_AUTH_MODE=
# --- Unified SASE OAuth 2.0 (PRISMA_AUTH_MODE=sase) ---
# Provision a service-app credential in Strata Cloud Manager → Identity &
# Access → Service Accounts. TSG_ID is the Tenant Service Group id shown
# in the same panel — it scopes the resulting bearer token to your tenant.
# Both defaults below are correct for the public SASE cloud; override for
# region-specific gateways if you're on one.
# PRISMA_CLIENT_ID=
# PRISMA_CLIENT_SECRET=
# PRISMA_TSG_ID=
# PRISMA_SASE_BASE_URL=https://api.sase.paloaltonetworks.com
# PRISMA_AUTH_URL=https://auth.apps.paloaltonetworks.com/oauth2/access_token
# --- Legacy CloudGenix session token (PRISMA_AUTH_MODE=legacy) ---
# Interactive-user credential (email + password). Kept for backward
# compatibility only — the SASE mode above is what new deployments should
# use because it's service-account-shaped instead of impersonating a
# human. Session tokens last ~8 hours; the client transparently
# re-authenticates on 401.
# PRISMA_EMAIL=
# PRISMA_PASSWORD=
# PRISMA_LEGACY_BASE_URL=https://api.cloudgenix.com
# --- Prisma client-side concurrency cap ---
# Maximum number of in-flight Prisma requests at any moment. Prisma
# throttles bursts hard — /voicediag fans out 10+ metric calls per
# request, and without this cap they'd all fire simultaneously and
# 429-cascade. Default 3 is empirically safe on the observed tenant.
# Bump if your tenant tolerates more parallelism (fewer 429s), lower
# if you still see them.
# PRISMA_MAX_INFLIGHT=3
# --- Prisma Strata Cloud Manager (SCM) UI base URL ---
# Used only for building "View in Prisma UI" deep links surfaced in
# the /phonestatus WAN follow-up + /voicediag per-app details. The
# API endpoints continue to use PRISMA_SASE_BASE_URL. Override this
# only for tenants on a partner-branded or region-specific SCM
# domain. Default matches the production SCM hostname.
# PRISMA_UI_BASE_URL=https://stratacloudmanager.paloaltonetworks.com
# --- WAN voice-quality thresholds (used by /voicediag WAN bucket + the
# /phonestatus WAN follow-up renderer) ---
# Defaults follow ITU-T G.114 (latency), G.711 PLC (loss), and RFC 3550
# (jitter) references. See services/voiceDiag/README.md for the standards
# reference table.
# WAN_STANDARD_LATENCY_WARN_MS=150
# WAN_STANDARD_LATENCY_ERROR_MS=400
# WAN_STANDARD_JITTER_WARN_MS=30
# WAN_STANDARD_JITTER_ERROR_MS=50
# WAN_STANDARD_LOSS_WARN_PCT=1
# WAN_STANDARD_LOSS_ERROR_PCT=3
# WAN_STANDARD_MOS_WARN=4.0
# WAN_STANDARD_MOS_ERROR=3.5
# WAN_STANDARD_HEALTHSCORE_WARN=80
# WAN_STANDARD_HEALTHSCORE_ERROR=60
# --- Per-application voice-quality thresholds ---
# These grade the WORST-window value in the 24h series (rather than
# the average) because for voice a brief 15-min degradation is a bad
# operator experience even if the daily avg looks fine. Defaults are
# less strict than the link-probe thresholds above — otherwise a
# single 5-min bad window would drown the operator in warnings.
# WAN_STANDARD_APP_MOS_WARN=4.0
# WAN_STANDARD_APP_MOS_ERROR=3.5
# WAN_STANDARD_APP_LOSS_WARN_PCT=5
# WAN_STANDARD_APP_LOSS_ERROR_PCT=15
# WAN_STANDARD_APP_JITTER_WARN_MS=30
# WAN_STANDARD_APP_JITTER_ERROR_MS=50
# --- Per-application DPI voice-metrics enablement ---
# When set, /phonestatus and /voicediag will fetch REAL voice-traffic
# quality metrics (MOS, packet loss, jitter, bandwidth) from Prisma's
# Application Path Details endpoint for the configured voice app. This
# is DPI data on actual RTP frames — the LQM link-probes above are
# synthetic probes across the WAN circuits and can look green while
# real audio is degrading (Prisma UI shows both; this env exposes the
# second one to the bot).
#
# Which app to point at is a tenant choice:
# - Webex_Calling_RTP ← recommended for Webex Calling shops. The
# Webex-specific DPI signature excludes noise
# from other UDP traffic and gives materially
# more accurate per-call quality signal.
# - rtp-base ← generic RTP DPI signature. Catches all RTP,
# including non-Webex flows; use if you have
# mixed voice apps or a tenant that doesn't
# break out per-provider signatures.
# - MS_Teams_RTP / Zoom_RTP / etc. — for those shops.
#
# To find the app id for your tenant:
# npm run prisma:probe -- appdefs webex # or `rtp`, `teams`, `zoom`
# or grab it from the Prisma UI's Application Path Details URL. Leave
# blank to disable the extra API calls entirely (checks return skipped
# with an explanation).
# PRISMA_APP_ID_VOICE=
# Optional human-friendly display label for the app configured above.
# Shown in the "Voice Traffic Quality (…)" section headers and check
# messages. Defaults to "voice" if unset. Recommend setting it to the
# exact Prisma UI app label so operators can cross-reference:
# PRISMA_APP_NAME_VOICE=Webex_Calling_RTP
# --- BACKWARDS-COMPAT: PRISMA_APP_ID_RTP_BASE (deprecated) ---
# Original name from the initial rtp-base-only release. Still honored
# if set AND PRISMA_APP_ID_VOICE is unset, but logs a one-time
# deprecation warning on first use. When falling back to this var, the
# display name defaults to "rtp-base" so existing dashboards keep
# reading the same label. Rename to PRISMA_APP_ID_VOICE at your
# convenience — the old var will be removed in a future release.
# PRISMA_APP_ID_RTP_BASE=
# --- Phase 2: per-waninterface voice DPI fan-out ---
# When true, collectSdwanForStore also fetches AppPerf* metrics filtered
# by filter.path=[waninterfaceId] (up to 4 circuits). Multiplies Prisma
# request count — leave off unless probing path attribution.
# Requires a tenant that accepts filter.path on AppPerfUDP* metrics
# (confirm with: npm run prisma:probe -- try-shapes app-by-path …).
# PRISMA_VOICE_PATH_ATTRIBUTION=false
# --- Phase 1 optional: voice DPI broken out by path_type (VPN vs DIA) ---
# Off by default — runs AFTER the core metric batch and only fetches
# loss for VPN + DirectInternet. Enable when you want overlay-vs-DIA
# comparison without reintroducing the parallel 429 storm.
# PRISMA_VOICE_PATH_TYPE_BREAKOUT=false
# --- WAN bucket global kill-switch (mirror of VOICE_STANDARD_ENABLED for
# the port bucket). Set to `false` to silence the entire WAN check
# bucket while a Prisma cleanup is in progress. The /phonestatus
# follow-up is unaffected — it always renders whatever Prisma
# returns. Feature-config + port-hygiene buckets keep running. ---
# WAN_STANDARD_ENABLED=true
# --- WAN look-back window (minutes) used when the operator doesn't
# pass `--window` on /voicediag (and always used by /phonestatus).
# Applies to healthscore + LQM + per-app voice DPI + alarms.
# Alarms window is max(60, WAN_STANDARD_WINDOW_MINUTES) since
# sub-hour alarm queries are usually too noisy to be actionable.
#
# Default: 10080 (7 days). Widened from 24h because per-app DPI
# metrics (Webex_Calling_RTP etc.) only generate datapoints WHEN
# CALLS HAPPEN — a store that takes 3-4 Webex calls per day gives
# only ~30 samples in 24h, well below the point where "worst-
# window" statistics mean anything. Seven days consistently
# yields 150-300+ per-app samples across stores of any size.
#
# For live-incident triage where you want a fresh snapshot, pass
# `--window 1h` or `--window 24h` on /voicediag (or lower this
# global default here).
#
# Interval selection is automatic per window size — see
# integrations/paloalto/metrics.js:pickAppMetricInterval /
# pickInterval. Hard-capped at 10080 (7d); beyond that Prisma
# downsamples to 1-day buckets and the worst-window signal
# dissolves. If you truly need 30d, edit MAX_WINDOW_MINUTES in
# services/enrichment/sdwanEnrichment.js and confirm the interval
# pickers still yield useful granularity for the check semantics.
#
# Common values: 60 (1h — live triage), 1440 (24h), 10080 (7d — default).
# WAN_STANDARD_WINDOW_MINUTES=10080
# -----------------------------------------------------------------------------
# /voicediag — Store voice-line standards
# -----------------------------------------------------------------------------
# Store phones are held to a fixed configuration standard. Most of the
# standard is baked into the per-check modules under
# services/voiceDiag/checks/ (see services/voiceDiag/README.md for the
# reference table) — the values below only exist for the pieces that
# vary per site / per environment.
#
# Expected VLAN for store phones. Today AE tags phones into the data
# VLAN (102); some sites may move onto a proper voice VLAN in the
# future. The check compares against whatever value is set here and
# emits a warn when a phone shows up on any other VLAN. Default 102
# if unset.
# VOICE_STANDARD_PHONE_VLAN=102
# Global kill-switch for /voicediag port-hygiene checks. Set to
# `false` to silence portType / portVlan / portPoe / portEnabled while
# an underlying Meraki cleanup is in progress and you don't want the
# noise. Values: true (default) / false. The feature-config checks
# (DND, forwarding, waiting, voicemail, intercept, hoteling, exec-
# assistant, outgoingPermission) always run — this only gates the
# switchport bucket.
# VOICE_STANDARD_ENABLED=true
# -----------------------------------------------------------------------------
# /webexhost — Webex Meetings host license helper
# -----------------------------------------------------------------------------
# Site to evaluate host status against. Default: aeo2go.webex.com.
# WEBEX_HOST_SITE_URL=aeo2go.webex.com
# License ID auto-assigned by `/webexhost <email>` when the user is missing a
# host license on the site. Discover the right id by running `/webexhost list`
# (lists every meeting license on the site with id + remaining seats).
# Until this is set, `/webexhost <email>` will still report status, but the
# confirm-assign step refuses with a clear message pointing at /webexhost list.
WEBEX_HOST_LICENSE_ID=
# -----------------------------------------------------------------------------
# Jira (required for /jira* commands)
# Use JIRA_CLOUD_ID for service accounts / new Atlassian API gateway endpoints:
# JIRA_CLOUD_ID=<uuid-from-atlassian>
# (constructs https://api.atlassian.com/ex/jira/<id>/rest/api/3 ...)
# Otherwise fall back to classic site base:
# JIRA_BASE_URL=https://your-org.atlassian.net
# -----------------------------------------------------------------------------
JIRA_CLOUD_ID=
JIRA_BASE_URL=https://your-org.atlassian.net
JIRA_EMAIL=your-email@company.com
JIRA_API_TOKEN=your-jira-api-token
JIRA_MAX_RESULTS=30
# -----------------------------------------------------------------------------
# Jira Poller (hourly ticket enrichment)
# -----------------------------------------------------------------------------
# Cron poller that scans unassigned tickets in the AV / Comm Services /
# Mobility queue every hour, posts a phone or AV status snapshot as a
# Jira comment on each store-scoped ticket, labels the ticket
# `bot-enriched` so it's not re-processed, and posts a summary of newly
# enriched tickets to a Webex space. Tickets the AI classifier decides
# are out-of-scope get labeled `bot-skipped` (same anti-retry purpose,
# no comment posted) so we don't burn AI tokens re-reading them every
# hour.
#
# Required scopes on JIRA_API_TOKEN: read + write on issues in the
# target projects (comment + edit-labels). The token owner needs "Add
# Comments" and "Edit Issues" permission — a plain read-only integration
# token WILL NOT work.
#
# JIRA_POLLER_ROOM_ID
# Webex space roomId to post the per-poll "N new tickets" summary to.
# Poller stays DISABLED (cron never registered) if unset — safe default
# for dev instances that share the same Jira credentials.
#
# JIRA_POLLER_PRIME_ON_START
# One-shot backlog-prime toggle. Set to `true` for a SINGLE deploy to
# bulk-label every ticket currently matching the poller's JQL as
# `bot-enriched` WITHOUT enriching them or posting a summary. Prevents
# day-one spam from a queue that already has dozens of open tickets.
# Flip back to `false` (or remove) before the next restart or the
# prime pass runs again.
#
# JIRA_STORE_FIELD_ID
# Optional. Numeric custom-field id for the `Store Number` field
# (e.g. `customfield_10042`). If unset, the poller discovers it at
# first use via GET /rest/api/3/field. Set explicitly to skip
# discovery (saves one API call at startup) or when the display
# name resolves ambiguously in your Jira schema.
#
# Note: on tenants where Store Number is an Atlassian Assets object
# reference (not a plain string), the field value the poller reads
# will be an opaque object like {"objectId":"81255"}. The AI
# classifier handles this by extracting the store number from the
# ticket summary / description text instead ("Store 3860 - ..."), so
# the field being unreadable is not fatal.
#
# JIRA_POLLER_MODEL
# Optional model override for the AI ticket classifier. Defaults to
# XAI_MODEL if unset. Classification is a small, deterministic
# structured task (~50-token JSON output per ticket) that doesn't
# need the reasoning depth of the summary model — a cheaper/faster
# model (e.g. `grok-3-mini`) saves noticeable money at scale without
# hurting classification accuracy on the phone/av/skip taxonomy.
# -----------------------------------------------------------------------------
JIRA_POLLER_ROOM_ID=
JIRA_POLLER_PRIME_ON_START=false
JIRA_STORE_FIELD_ID=
JIRA_POLLER_MODEL=
# -----------------------------------------------------------------------------
# xAI / Grok (used for ticket and work order summarization)
# -----------------------------------------------------------------------------
XAI_URL=https://api.x.ai/v1/chat/completions
XAI_API_KEY=your-xai-api-key
XAI_MODEL=grok-2-latest
# -----------------------------------------------------------------------------
# Meraki
# -----------------------------------------------------------------------------
MERAKI_API_KEY=your-meraki-api-key
MERAKI_ORG_ID=your-org-id
# -----------------------------------------------------------------------------
# RED (Digital Signage)
# Comma-separated list of company IDs (one per company tenant).
# -----------------------------------------------------------------------------
RED_BASE_URL=https://api.red.com
RED_CLIENT_ID=your-red-client-id
RED_API_KEY=your-red-api-key
RED_COMPANY_IDS=company-id-1,company-id-2,company-id-3
# -----------------------------------------------------------------------------
# OptiSigns
# -----------------------------------------------------------------------------
OPTISIGN_API_KEY=your-optisigns-api-key
# -----------------------------------------------------------------------------
# DigiCert (for VC provisioning)
# -----------------------------------------------------------------------------
DIGICERT_API_KEY=your-digicert-key
DIGICERT_BASE_URL=https://one.digicert.com
DIGICERT_PROFILE_ID=your-profile-id
DIGICERT_SEAT_EMAIL=your-email@company.com
# -----------------------------------------------------------------------------
# MDM / Workspace ONE (two instances)
# -----------------------------------------------------------------------------
# Standard / Store MDM
WS1_CLIENT_ID=...
WS1_CLIENT_SECRET=...
WS1_TENANT_CODE=...
# CORP MDM (used for offboarding / enterprise wipes)
CORP_WS1_API_BASE=https://...
CORP_WS1_CLIENT_ID=...
CORP_WS1_CLIENT_SECRET=...
CORP_WS1_TENANT_CODE=...
# -----------------------------------------------------------------------------
# Atlas
# -----------------------------------------------------------------------------
ATLAS_AUTH_KEY=your-atlas-key
# -----------------------------------------------------------------------------
# ServiceChannel
# OAuth password grant against ServiceChannel's identity endpoint.
# -----------------------------------------------------------------------------
SC_BASE_URL=https://api.servicechannel.com/v3
SC_OAUTH_URL=https://login.servicechannel.com/oauth/token
SC_CLIENT_ID=your-sc-client-id
SC_CLIENT_SECRET=your-sc-client-secret
SC_USERNAME=your-sc-username@company.com
SC_PASSWORD=your-sc-password
# -----------------------------------------------------------------------------
# VC Provisioning "backdoor" account
# Used by vcProvisionService for local-device authentication during certificate
# enrollment. DIGICERT_SEAT_EMAIL above is unrelated.
# -----------------------------------------------------------------------------
BACKDOOR_USERNAME=monitor
BACKDOOR_PASSWORD=...
# -----------------------------------------------------------------------------
# Cisco DBS-210 DECT — LOCAL DEV TEST HARNESS
# Used only by scripts/testDectBase.js when iterating locally against a
# lab base. NOT read by the bot at runtime — the bot never talks to a
# DBS-210 directly. Actual production DBS-210 access lives in the
# DECT_RELAY_* env below and is executed by dect-relay-agent/.
#
# Cisco's guidance: use the DECT serviceability password (Control Hub
# → Calling → Features → DECT Networks → Manage → Manage DECT
# serviceability password). Our tenant is configured to share a
# single password across all bases in the fleet.
# -----------------------------------------------------------------------------
DECT_TEST_BASE_IP=10.0.0.100
DECT_TEST_USER=admin
DECT_TEST_PASSWORD=your-dect-serviceability-password
# Optional: seconds to wait for base station responses. DBS-210 is slow
# on syslog/PRT downloads; 30s is a good starting point.
DECT_TEST_TIMEOUT_MS=30000
# -----------------------------------------------------------------------------
# DECT Relay Hub — WSS endpoint for the data-center relay agent
# The bot process runs in the public cloud and can't reach 10.x. The
# relay agent (see dect-relay-agent/) runs INSIDE the DC, dials
# outbound over WSS to this bot, and executes any DECT command the bot
# pushes. Feature-gated: without DECT_RELAY_AGENT_TOKEN, the WSS
# endpoint is not attached and /phonestatus's DECT follow-up + the
# future /dectstatus command return "relay not connected".
#
# Rotate DECT_RELAY_AGENT_TOKEN on both sides at once. Suggested
# generation: `openssl rand -hex 32`.
# -----------------------------------------------------------------------------
DECT_RELAY_AGENT_TOKEN=
# Optional. Default: /dect-relay/ws. Change only if you also change
# DECT_RELAY_BOT_URL on the agent side to match.
# DECT_RELAY_PATH=/dect-relay/ws
# Capture raw status.xml from a live store (bot + relay must be running):
# node scripts/fetchDectStatusXml.js 782 --save-dir tests/fixtures/dect/
# Uses GET /api/dect/raw-xml/:storeNumber (collect-raw via relay).
# -----------------------------------------------------------------------------
# Twilio /calltest — outbound voice path testing
# -----------------------------------------------------------------------------
# Places real PSTN calls via Twilio. Requires a public HTTPS base URL Twilio
# can reach for TwiML + status webhooks. Must match your reverse-proxy path
# exactly (no trailing slash). Example for NGINX Proxy Manager location
# /CollabSupport/ → http://bot-host:1800:
#
# TWILIO_ACCOUNT_SID=
# TWILIO_AUTH_TOKEN=
# TWILIO_FROM_NUMBER=+1...
# TWILIO_WEBHOOK_BASE_URL=https://your-public-host.example.com/CollabSupport
# CALLTEST_ENABLED=true
#
# Store entry (AA path only):
# CALLTEST_DTMF=1
# CALLTEST_GREETING_PAUSE_SEC=5
# CALLTEST_ANSWER_WAIT_SEC=45
# CALLTEST_DIAL_TIMEOUT_SEC=30
#
# Shared 60s test core (both store + dial modes):
# CALLTEST_LISTEN_SEC=60
# CALLTEST_INTRO_TTS=Hello. This is an automated connectivity test...
# CALLTEST_OUTRO_TTS=Thank you. The connectivity test is complete. Goodbye.
#
# Post-call enrichment (separate Webex cards after the result):
# CALLTEST_TWILIO_ENRICH=true
# CALLTEST_TWILIO_DETAILS_DELAY_MS=10000
# CALLTEST_TWILIO_INSIGHTS_DELAY_MS=360000
# CALLTEST_TWILIO_INSIGHTS_RETRIES=3
# CALLTEST_TWILIO_INSIGHTS_RETRY_MS=30000
#
# Webex CDR leg match (store + dial when dialed number is a known location DID):
# CALLTEST_CDR_ENRICH=true
# CALLTEST_CDR_DELAY_MS=360000
# CALLTEST_CDR_MATCH_BUFFER_MS=120000
#
# Per-store entry overrides: config/calltest-stores.json
# -----------------------------------------------------------------------------
# /callreport — daily call digest (CDR + Media Quality + Prisma WAN)
# -----------------------------------------------------------------------------
# CALLREPORT_BUSINESS_START_HOUR=9
# CALLREPORT_BUSINESS_END_HOUR=21
# CALLREPORT_API_LAG_MS=300000
# CALLREPORT_MAX_DETAIL_ROWS=25
# CALLREPORT_REPORT_POLL_MS=5000
# CALLREPORT_REPORT_POLL_MAX_MS=180000
# (VOICEREPORT_* env vars still accepted for backward compatibility)
# WEBEX_REPORT_TEMPLATE_MEDIA_QUALITY=
# -----------------------------------------------------------------------------
# Optional. Per-base collect() RPC timeout. Corporate proxies can make
# DBS-210 reads slow; 15s is comfortable, 30s is generous.
# DECT_COLLECT_TIMEOUT_MS=15000
# -----------------------------------------------------------------------------
# Notes
# -----------------------------------------------------------------------------
# - config/config.json has been fully removed. All configuration is via env vars.
# - The rotating Webex service token lives in config/webex-service-tokens.json
# and is mounted separately when running in Docker.
# - Add any new integration keys above following the same pattern.