# Copy to `.env` for local docker-compose runs, OR enter these values in # Portainer's Stack → Environment variables UI. # # All variables have defaults baked into compose.yaml — this file is the # documentation of what's configurable, not a hard requirement. # --------------------------------------------------------------------- # tcp-ingestion # --------------------------------------------------------------------- # Image tag to pull. `main` auto-tracks the latest commit on the main branch. # In production, pin to a specific commit SHA for reproducibility. # Example: TCP_INGESTION_TAG=af06973 TCP_INGESTION_TAG=main # Instance identifier — must be stable across the lifetime of the process. # Phase 2's connection registry depends on this; keep it unique per deployed # instance (e.g. `stage-1`, `stage-2`, `prod-eu-1`). TCP_INGESTION_INSTANCE_ID=stage-1 # Host port that GPS devices connect to. The container always listens on 5027 # internally; this maps it to a host port. If multiple stacks run on one host, # give each a distinct host port (e.g. 5028, 5029). TCP_INGESTION_PORT=5027 # --------------------------------------------------------------------- # postgres / TimescaleDB # --------------------------------------------------------------------- # Database superuser, password, and default database. Created on first # boot of the postgres container; the volume persists them after that. # IMPORTANT: change POSTGRES_PASSWORD before deploying to production. # Changing it after first boot has no effect — the volume already has # the password baked in. To rotate, ALTER USER inside psql. POSTGRES_USER=trm POSTGRES_PASSWORD=trm-pilot-change-me POSTGRES_DB=trm # --------------------------------------------------------------------- # processor # --------------------------------------------------------------------- # Image tag to pull. `main` auto-tracks the latest commit on the main branch. # In production, pin to a specific commit SHA for reproducibility. # Example: PROCESSOR_TAG=9791620 PROCESSOR_TAG=main # Instance identifier — must be stable across the lifetime of the process, # AND unique per running instance (it's used as the Redis consumer-group # member name; two instances with the same name will read from the same # Pending Entries List, which is undefined behaviour). PROCESSOR_INSTANCE_ID=processor-1 # --------------------------------------------------------------------- # directus (business plane) # --------------------------------------------------------------------- # Image tag to pull. `main` auto-tracks the latest commit on the main branch. # In production, pin to a specific commit SHA for reproducibility. # Example: DIRECTUS_TAG=ef8bd91 DIRECTUS_TAG=main # Note: directus is intentionally NOT host-published. The admin UI + API # listen on port 8055 inside the `trm_default` Compose network only, # reachable as `http://directus:8055` from a reverse proxy (Traefik / # Caddy / nginx) on the host or attached to the same network. Wire your # proxy to forward your public domain to that internal address; the # proxy handles TLS, auth headers, and any WAF / rate-limit policy. # For local dev (compose.dev.yaml in trm/directus) the dev compose # host-publishes 8055 directly — this prod stack does not. # REQUIRED. Instance identity key (any UUID) and JWT signing secret # (long random string). Generate fresh values per environment: # DIRECTUS_KEY=$(uuidgen) # DIRECTUS_SECRET=$(openssl rand -hex 64) # Two instances sharing these produce colliding tokens — never reuse # stage's KEY/SECRET in production. The compose defaults are obvious # placeholders and will fail on any meaningful KEY validation. DIRECTUS_KEY=REPLACE-ME-WITH-A-UUID DIRECTUS_SECRET=REPLACE-ME-WITH-A-LONG-RANDOM-STRING # First-boot admin user. Created automatically when directus_users is # empty at first boot; ignored on subsequent boots. Change the password # via the admin UI after first login (the password env var is NOT a # rotation mechanism — only the initial seed). DIRECTUS_ADMIN_EMAIL=admin@example.com DIRECTUS_ADMIN_PASSWORD=CHANGE-ON-FIRST-LOGIN # Public-facing URL used in password-reset emails, OAuth redirects, and # asset URLs. In real prod set to https://; the localhost # default is for first-deploy smoke testing only. DIRECTUS_PUBLIC_URL=http://localhost:8055 # Optional toggles. Defaults disable cache and CORS. Enable per env: # DIRECTUS_CACHE_ENABLED=true (then configure CACHE_STORE etc. directly # in compose.yaml — Directus has 20+ # cache-related env vars not exposed here) # DIRECTUS_CORS_ENABLED=true # DIRECTUS_CORS_ORIGIN=https://your-spa.example.com DIRECTUS_CACHE_ENABLED=false DIRECTUS_CORS_ENABLED=false DIRECTUS_CORS_ORIGIN=false # pino log style: json (structured, for log aggregators) | pretty (human-readable). # Defaults to json in compose.yaml — production-friendly. Set to `pretty` # for local debugging. LOG_STYLE=json # --------------------------------------------------------------------- # spa (React front-end) # --------------------------------------------------------------------- # Image tag to pull. `main` auto-tracks the latest commit on the main branch. # In production, pin to a specific commit SHA for reproducibility. # Example: SPA_TAG=ab12cd3 SPA_TAG=main # Path on the host to the runtime config file. Mounted read-only into the # SPA container at /usr/share/nginx/html/config.json. Defaults to a sibling # file in this repo; create it from spa-config.example.json before first # deploy: # cp spa-config.example.json spa-config.json # # edit env to "stage" or "prod"; add googleMapsKey if operators have one # In Portainer, mount via a host-path volume that points at the file. SPA_CONFIG_FILE=./spa-config.json # Note: the SPA is intentionally NOT host-published. nginx serves the # bundle on port 80 inside the trm_default Compose network only, # reachable as `http://spa:80` from the same reverse proxy that fronts # directus. The proxy routes `/api` + `/ws-business` + `/ws-live` to the # backends and everything else to the SPA — same-origin is required for # the cookie-based auth flow to work end-to-end. # --------------------------------------------------------------------- # Shared # --------------------------------------------------------------------- # Redis Stream name carrying normalized Position records from # tcp-ingestion to processor. Both services must use the same value. # Override only if migrating to a new stream (e.g. for multi-vendor). REDIS_TELEMETRY_STREAM=telemetry:teltonika # pino log level: fatal | error | warn | info | debug | trace LOG_LEVEL=info