Files
gods-eye-view/.env.example
T
1fc795577a feat(sdr): local receivers — browser RTL-SDR, Local ADS-B layer and decoder feeds (#732)
Adds a Local ADS-B layer fed by your own receivers: a browser WebUSB
RTL-SDR (with FM listening) and local 1090/978 MHz decoder feeds
(LOCAL_RECEIVER_FEEDS, local and private addresses only). Local aircraft
draw in magenta with class, silhouettes, 3D models, trails and a position
sanity filter; the click card names the band and input that heard them.

Builds on Sameh Khamis's SDR prototype and the initial GEV port by
Mazeyar Moeini Feizabadi (#134); the decoder-feed design follows the
notes on #57. Closes #57.

Co-authored-by: Sameh Khamis <[email protected]>
Co-authored-by: Mazeyar Moeini Feizabadi <[email protected]>
2026-09-23 16:00:53 -05:00

186 lines
11 KiB
Bash

# Local development uses one file for browser and server keys.
# Copy to .env (ignored); this example contains names and placeholders only.
# Only explicitly selected browser keys are injected into the client bundle.
# God's Eye View — environment variables
# Copy to .env and fill in your keys. On macOS the launcher can also read keys
# from the Keychain (see README); on Linux/Windows use this file or env vars.
#
# Easiest path: don't edit anything. Run the app and paste keys into the
# in-app Provider Settings panel (the POWER UP chip, bottom-right) — it writes
# this checkout's .env for you (owner-only permissions) and restarts the dev
# server. Under the Pinokio launcher it writes pinokio/ENVIRONMENT instead.
# Keys you supply yourself (shell env, Keychain) are shown as configured
# externally and never touched. This file remains the reference for headless
# and self-hosted setups.
#
# NOTE ON CLIENT-EXPOSED KEYS: GOOGLE_MAPS_API_KEY and CESIUM_ION_TOKEN are
# injected into the browser bundle by design (they're used client-side) and
# WILL be visible in devtools. Restrict/scope them rather than trying to hide
# them (see SECURITY.md). All other keys below stay server-side.
# Optional: direct Google Photorealistic 3D Tiles and GEV place search.
# Without it, a Cesium ion token can still load ion-hosted Google 3D; with
# neither credential, the app starts on keyless Esri World Imagery with OSM
# available in the map tray and as the automatic provider-failure fallback.
# CLIENT-EXPOSED — restrict it (HTTP referrer + API restriction) in Google Cloud.
GOOGLE_MAPS_API_KEY=
# Optional: a SEPARATE Google API key for the SERVER-SIDE Google calls — Places
# (/api/google/nearby-places, /api/google/text-search) and the CCTV Street View
# fallback (#33). It never reaches the browser, so it can be restricted by
# server IP (not HTTP referrer) and scoped to just Places API + Street View
# Static API, letting the client-exposed key above stay narrowly scoped to Map
# Tiles + Geocoding. Unset, these calls fall back to GOOGLE_MAPS_API_KEY, so a
# single shared key keeps working — splitting is opt-in.
# SERVER-SIDE ONLY.
GOOGLE_MAPS_SERVER_API_KEY=
# Optional: opt-in per-IP rate limit for the Google Places cost endpoint
# (/api/google/nearby-places), in requests per minute.
# DEFAULT IS UNLIMITED — unset (or 0) means no throttling, unchanged behavior.
# Set a positive integer to cap per-IP Places quota spend when self-hosting.
# Recommended whenever HOST is not localhost. NOTE: this is a per-IP,
# process-local guard (in-memory, resets on restart; over-limit requests get a
# sanitized 429) — it is NOT a billing cap. For hard spend protection set
# provider-side budget alerts: Google Cloud Console -> Billing -> Budgets & alerts
# (plus per-API quotas under APIs & Services -> Quotas).
# GEV_RATELIMIT_GOOGLE_PER_MIN=60
# Optional: Google Photorealistic 3D Tiles through Cesium ion, Bing world
# imagery, and Cesium World Terrain. The free Community plan is for eligible
# personal/non-commercial use and has quotas; check current Cesium terms. A
# direct Google key above is still required for GEV place search.
# CLIENT-EXPOSED — use a public assets:read token with URL restrictions.
CESIUM_ION_TOKEN=
# Optional: OpenAI Realtime voice control. Do not prefix with VITE_.
OPENAI_API_KEY=
OPENAI_REALTIME_MODEL=gpt-realtime-2
# Cheaper voice tier, used when the GEV MIC panel's MINI toggle is on.
# Model ids and prices are external facts that drift — if OpenAI moves this id,
# override it here rather than editing src/voice/voiceCost.js.
OPENAI_REALTIME_MODEL_MINI=gpt-realtime-2.1-mini
OPENAI_REALTIME_VOICE=marin
OPENAI_REALTIME_REASONING_EFFORT=low
# Keep only a short conversational window. Current map state is fetched live.
OPENAI_REALTIME_CONTEXT_TOKENS=3000
OPENAI_REALTIME_CONTEXT_RETENTION=0.5
OPENAI_HUD_SUMMARY_MODEL=gpt-5-nano
# Optional: opt-in per-IP rate limit for the OpenAI cost endpoints
# (/api/realtime/token + /api/openai/hud-summary), in requests per minute.
# DEFAULT IS UNLIMITED — unset (or 0) means no throttling, unchanged behavior.
# Set a positive integer to cap per-IP spend when self-hosting on a network.
# Recommended whenever HOST is not localhost. NOTE: this is a per-IP,
# process-local guard (in-memory, resets on restart; over-limit requests get a
# sanitized 429) — it is NOT a billing cap. For hard spend protection set a
# provider-side budget: OpenAI platform -> Settings -> Limits (usage limits).
# GEV_RATELIMIT_OPENAI_PER_MIN=30
# Optional: OpenSky auth mode (oauth | basic | auto | anon)
# Default: oauth — requires client credentials below
# Set to "anon" to skip aircraft auth (rate-limited but works)
OPENSKY_AUTH_MODE=oauth
# Optional: OpenSky OAuth credentials (from opensky-network.org account dashboard)
OPENSKY_CLIENT_ID=
OPENSKY_CLIENT_SECRET=
# Optional: higher Launch Library 2 request allowance. Public access works
# without a token.
LL2_API_TOKEN=
# Optional: OpenSky credentials JSON file path (alternative to above)
# OPENSKY_CREDENTIALS_FILE=/path/to/credentials.json
# Optional: dev server config
PORT=4173
# Network binding. Default is localhost — the server is reachable ONLY from
# this machine. This is the safe default: the dev server brokers your
# configured API keys, so anyone who can reach it can spend your quota.
# LAN opt-in: set HOST=0.0.0.0 to share on your local network. WARNING: that
# exposes every key-brokering proxy to everyone on the network — use only on
# networks you trust, and consider the GEV_RATELIMIT_* throttles above.
# HOST=localhost
# HOST=0.0.0.0
# Optional: NASA FIRMS live active fires. SERVER-SIDE ONLY (never reaches the
# browser — the client polls the /api/firms proxy). Free key:
# https://firms.modaps.eosdis.nasa.gov/api/map_key/
# Without a key the FIRMS Active Fires layer is empty ("KEY REQUIRED").
# FIRMS_MAP_KEY=
# Optional: Live AIS vessels layer
# Browser fetch path. Keep same-origin so the private stream key stays server-side.
VITE_AIS_LIVE_API_URL=/api/ais-live
# Client render cap. The server can keep a larger AISStream cache, but drawing
# every global vessel at once can overwhelm WebGL on laptops.
VITE_AIS_LIVE_MAX_ROWS=12000
# Active label cap after screen-space clustering. Labels exist for all rendered
# ships, but only clustered winners are shown to keep text readable.
VITE_AIS_LIVE_LABEL_MAX_ROWS=900
# Private AISStream key for the server-side websocket. Do not prefix with VITE_.
AISSTREAM_API_KEY=
# Optional AISStream subscription filters.
# AISSTREAM_BOUNDING_BOXES=[[[-90,-180],[90,180]]]
# AISSTREAM_MESSAGE_TYPES=PositionReport,StandardClassBPositionReport,ExtendedClassBPositionReport,ShipStaticData,StaticDataReport
# Feed watchdog: silence (ms) after which the feed is REPORTED stale; the socket
# is recycled at 2.5x that. Default 120000. The watchdog self-arms only for the
# default worldwide subscription — a narrow filter above can be legitimately
# quiet, so set this explicitly (sized for your filter) to opt back in.
# 0 disables the silence watch entirely.
# AISSTREAM_SILENCE_TIMEOUT_MS=120000
# Optional: local ADS-B decoder feeds for the Local ADS-B layer. SERVER-SIDE
# ONLY. Comma-separated band=url entries; band is 1090 or 978 (UAT). Each host
# must be loopback, a private (RFC1918) address, localhost or a *.local name;
# the path must end in aircraft.json. Invalid entries are logged and skipped.
# See docs/LOCAL-RECEIVERS.md.
# LOCAL_RECEIVER_FEEDS=1090=http://localhost:8080/data/aircraft.json,978=http://localhost:8978/data/aircraft.json
# Optional: TomTom live traffic flow (BYOK freemium — the free tier is 200,000
# Traffic Flow & Incidents tile requests per MONTH, not per day; no credit card
# required. Get a key at https://developer.tomtom.com). SERVER-SIDE ONLY:
# the browser fetches same-origin /api/tomtom/* and never sees the key.
# Keyless fallback: the traffic layer runs its built-in simulation (white dots,
# hardcoded per-road-class speeds) — no key required for the layer to work.
# TOMTOM_API_KEY=
# Optional soft cap on upstream tile fetches per UTC day (default 6000) — a
# configurable application safety ceiling, not a guarantee of staying within
# TomTom's free allowance (currently 200K tile requests/month:
# https://docs.tomtom.com/pricing/). 6000/day keeps a 31-day month at 186K,
# inside that allowance. Over the cap the proxy serves cached/stale tiles
# instead of hitting upstream.
# TOMTOM_DAILY_TILE_BUDGET=6000
# Optional: CCTV layer tuning (advanced). Defaults are sensible — leave unset
# unless you're customizing the camera source pack.
# CCTV_SOURCES_FILE=config/cctv_sources.austin.json # path to a source-pack JSON
# CCTV_SOURCES_JSON= # inline source-pack JSON (overrides file)
# CCTV_AUSTIN_ROWS_URL= # override the Austin Open Data catalog URL
# CCTV_AUSTIN_MAX_SOURCES=250 # cap cameras loaded from the Austin catalog
# CCTV_CALTRANS_DISTRICTS=4,7,11,3 # Caltrans districts; empty string disables Caltrans
# CCTV_CALTRANS_MAX_SOURCES=300 # cap cameras loaded from Caltrans
# CCTV_TFL_ENABLED=1 # set 0 to disable Transport for London JamCams
# CCTV_TFL_MAX_SOURCES=250 # cap cameras loaded from TfL
# CCTV_ONTARIO_ENABLED=1 # set 0 to disable Ontario 511 cameras
# CCTV_ONTARIO_MAX_SOURCES=1000 # cap cameras loaded from Ontario 511
# CCTV_FINTRAFFIC_ENABLED=1 # set 0 to drop the Fintraffic (Finland) pack
# CCTV_FINTRAFFIC_MAX_SOURCES=300 # cap cameras loaded from the Fintraffic catalog
# CCTV_DRIVEBC_MAX_SOURCES=250 # cap DriveBC (British Columbia) cameras, nearest Vancouver/Victoria first
# CCTV_DRIVEBC_ENABLED=1 # 0 turns off the DriveBC camera pack
# CCTV_TXDOT_ENABLED=1 # set 0 to disable the TxDOT (Texas) pack
# CCTV_TXDOT_DISTRICTS=AUS,SAT # TxDOT district codes (25 exist); empty disables the pack
# CCTV_TXDOT_MAX_SOURCES=500 # cap cameras loaded from the TxDOT catalogs
# CCTV_TALLINN_ENABLED=1 # set 0 to disable the Tallinn (Estonia) intersection cameras
# CCTV_TALLINN_MAX_SOURCES=255 # cap cameras loaded from the Tallinn catalog
# CCTV_TARKTEE_ENABLED=1 # set 0 to disable the Tarktee (Estonia) road-weather cameras
# CCTV_TARKTEE_MAX_SOURCES=179 # cap cameras loaded from Tarktee
# CCTV_WARENDORF_ENABLED=1 # set 0 to disable the Warendorf (Germany) municipal webcams
# CCTV_NSW_ENABLED=1 # set 0 to disable the Live Traffic NSW (Australia) cameras
# CCTV_NSW_MAX_SOURCES=250 # cap cameras loaded from Live Traffic NSW
# CCTV_CALGARY_ENABLED=1 # set 0 to disable the Open Calgary (Alberta) cameras
# CCTV_CALGARY_MAX_SOURCES=220 # cap cameras loaded from the Open Calgary catalog
# CCTV_CALGARY_ROWS_URL= # override the Open Calgary catalog URL
# CCTV_MAX_SOURCES=4000 # catalog-wide ceiling, shared round-robin across packs
# CCTV_PREFER_AUSTIN=1 # prioritize Austin cameras in the registry
# CCTV_FORCE_AUSTIN= # force live default sources when a source-pack JSON is configured