services: meshmap: build: ./backend container_name: meshmap-live ports: - "${WEB_PORT:-8080}:8080" environment: DEBUG_PAYLOAD: "${DEBUG_PAYLOAD:-false}" DEBUG_PAYLOAD_MAX: "${DEBUG_PAYLOAD_MAX:-400}" PAYLOAD_PREVIEW_MAX: "${PAYLOAD_PREVIEW_MAX:-800}" STATE_DIR: "${STATE_DIR:-/data}" DEVICE_COORDS_FILE: "${DEVICE_COORDS_FILE:-/data/device_coords.json}" NEIGHBOR_OVERRIDES_FILE: "${NEIGHBOR_OVERRIDES_FILE:-/data/neighbor_overrides.json}" CHANNEL_SECRETS_FILE: "${CHANNEL_SECRETS_FILE:-/data/channel_secrets.json}" BACKUP_ENABLED: "${BACKUP_ENABLED:-false}" BACKUP_INTERVAL_SECONDS: "${BACKUP_INTERVAL_SECONDS:-43200}" BACKUP_DIR: "${BACKUP_DIR:-/backup}" BACKUP_RETENTION_DAYS: "${BACKUP_RETENTION_DAYS:-7}" STATE_SAVE_INTERVAL: "${STATE_SAVE_INTERVAL:-5}" SITE_TITLE: "${SITE_TITLE:-Greater Boston Mesh Live Map}" SITE_DESCRIPTION: "${SITE_DESCRIPTION:-Live view of Greater Boston Mesh nodes, message routes, and advert paths.}" SITE_OG_IMAGE: "${SITE_OG_IMAGE:-}" SITE_URL: "${SITE_URL:-/}" SITE_ICON: "${SITE_ICON:-/static/logo.png}" SITE_FEED_NOTE: "${SITE_FEED_NOTE:-Feed: Boston MQTT.}" CUSTOM_LINK_URL: "${CUSTOM_LINK_URL:-}" PACKET_ANALYZER_URL: "${PACKET_ANALYZER_URL:-}" QR_CODE_BUTTON_ENABLED: "${QR_CODE_BUTTON_ENABLED:-false}" PEERS_DEFAULT_LIMIT: "${PEERS_DEFAULT_LIMIT:-8}" PROD_MODE: "${PROD_MODE:-false}" PROD_TOKEN: "${PROD_TOKEN:-}" GIT_CHECK_ENABLED: "${GIT_CHECK_ENABLED:-false}" GIT_CHECK_FETCH: "${GIT_CHECK_FETCH:-false}" GIT_CHECK_PATH: "${GIT_CHECK_PATH:-/app}" GIT_CHECK_INTERVAL_SECONDS: "${GIT_CHECK_INTERVAL_SECONDS:-43200}" DISTANCE_UNITS: "${DISTANCE_UNITS:-km}" NODE_MARKER_RADIUS: "${NODE_MARKER_RADIUS:-8}" MQTT_HOST: "${MQTT_HOST:-localhost}" MQTT_PORT: "${MQTT_PORT:-1883}" MQTT_USERNAME: "${MQTT_USERNAME:-}" MQTT_PASSWORD: "${MQTT_PASSWORD:-}" MQTT_TRANSPORT: "${MQTT_TRANSPORT:-tcp}" MQTT_WS_PATH: "${MQTT_WS_PATH:-/mqtt}" MQTT_TLS: "${MQTT_TLS:-false}" MQTT_TOPIC: "${MQTT_TOPIC:-meshcore/#}" DEVICE_TTL_HOURS: "${DEVICE_TTL_HOURS:-96}" PATH_TTL_SECONDS: "${PATH_TTL_SECONDS:-172800}" HEAT_TTL_SECONDS: "${HEAT_TTL_SECONDS:-600}" TRAIL_LEN: "${TRAIL_LEN:-30}" ROUTE_TTL_SECONDS: "${ROUTE_TTL_SECONDS:-120}" ROUTE_INFRA_ONLY: "${ROUTE_INFRA_ONLY:-true}" ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK: "${ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK:-false}" ROUTE_MAX_HOP_DISTANCE: "${ROUTE_MAX_HOP_DISTANCE:-100}" ROUTE_HISTORY_ALLOWED_MODES: "${ROUTE_HISTORY_ALLOWED_MODES:-path,direct,fanout}" MESSAGE_ORIGIN_TTL_SECONDS: "${MESSAGE_ORIGIN_TTL_SECONDS:-300}" MQTT_ONLINE_SECONDS: "${MQTT_ONLINE_SECONDS:-300}" MQTT_ONLINE_STATUS_TTL_SECONDS: "${MQTT_ONLINE_STATUS_TTL_SECONDS:-300}" MQTT_ONLINE_INTERNAL_TTL_SECONDS: "${MQTT_ONLINE_INTERNAL_TTL_SECONDS:-300}" MQTT_ACTIVITY_PACKETS_TTL_SECONDS: "${MQTT_ACTIVITY_PACKETS_TTL_SECONDS:-300}" MQTT_STATUS_OFFLINE_VALUES: "${MQTT_STATUS_OFFLINE_VALUES:-offline,disconnected}" MQTT_SEEN_BROADCAST_MIN_SECONDS: "${MQTT_SEEN_BROADCAST_MIN_SECONDS:-5}" MAP_START_LAT: "${MAP_START_LAT:-42.3601}" MAP_START_LON: "${MAP_START_LON:--71.1500}" MAP_START_ZOOM: "${MAP_START_ZOOM:-10}" MAP_RADIUS_KM: "${MAP_RADIUS_KM:-0}" MAP_RADIUS_SHOW: "${MAP_RADIUS_SHOW:-false}" MAP_BOUNDARY_MODE: "${MAP_BOUNDARY_MODE:-radius}" MAP_BOUNDARY_FILE: "${MAP_BOUNDARY_FILE:-/data/map_boundary.json}" MAP_BOUNDARY_SHOW: "${MAP_BOUNDARY_SHOW:-false}" MAP_DEFAULT_LAYER: "${MAP_DEFAULT_LAYER:-light}" LOS_ELEVATION_URL: "${LOS_ELEVATION_URL:-https://api.opentopodata.org/v1/srtm90m}" LOS_ELEVATION_PROXY_URL: "${LOS_ELEVATION_PROXY_URL:-/los/elevations}" LOS_SAMPLE_MIN: "${LOS_SAMPLE_MIN:-10}" LOS_SAMPLE_MAX: "${LOS_SAMPLE_MAX:-80}" LOS_SAMPLE_STEP_METERS: "${LOS_SAMPLE_STEP_METERS:-250}" ELEVATION_CACHE_TTL: "${ELEVATION_CACHE_TTL:-21600}" LOS_CURVATURE_ENABLED: "${LOS_CURVATURE_ENABLED:-true}" LOS_CURVATURE_FACTOR: "${LOS_CURVATURE_FACTOR:-1.333333}" LOS_PEAKS_MAX: "${LOS_PEAKS_MAX:-4}" COVERAGE_API_URL: "${COVERAGE_API_URL:-}" COVERAGE_API_KEY: "${COVERAGE_API_KEY:-}" COVERAGE_MAX_AGE_DAYS: "${COVERAGE_MAX_AGE_DAYS:-30}" COVERAGE_RATE_LIMIT_COOLDOWN_SECONDS: "${COVERAGE_RATE_LIMIT_COOLDOWN_SECONDS:-3600}" COVERAGE_CACHE_FILE: "${COVERAGE_CACHE_FILE:-/data/coverage_cache.json}" COVERAGE_SYNC_INTERVAL_SECONDS: "${COVERAGE_SYNC_INTERVAL_SECONDS:-3600}" WEATHER_RADAR_ENABLED: "${WEATHER_RADAR_ENABLED:-true}" WEATHER_RADAR_COUNTRY_BOUNDS_ENABLED: "${WEATHER_RADAR_COUNTRY_BOUNDS_ENABLED:-false}" WEATHER_RADAR_COUNTRY_LOOKUP_URL: "${WEATHER_RADAR_COUNTRY_LOOKUP_URL:-/weather/radar/country-bounds}" WEATHER_WIND_ENABLED: "${WEATHER_WIND_ENABLED:-true}" WEATHER_WIND_API_URL: "${WEATHER_WIND_API_URL:-https://api.open-meteo.com/v1/forecast}" WEATHER_WIND_GRID_SIZE: "${WEATHER_WIND_GRID_SIZE:-3}" WEATHER_WIND_REFRESH_SECONDS: "${WEATHER_WIND_REFRESH_SECONDS:-180}" TURNSTILE_ENABLED: "${TURNSTILE_ENABLED:-false}" TURNSTILE_SITE_KEY: "${TURNSTILE_SITE_KEY:-}" TURNSTILE_SECRET_KEY: "${TURNSTILE_SECRET_KEY:-}" TURNSTILE_API_URL: "${TURNSTILE_API_URL:-https://challenges.cloudflare.com/turnstile/v0/siteverify}" TURNSTILE_TOKEN_TTL_SECONDS: "${TURNSTILE_TOKEN_TTL_SECONDS:-86400}" restart: unless-stopped volumes: - ./data:/data - ./backup:/backup - ./:/repo:ro