# Copy to .env for docker compose use. # DO NOT commit .env (it carries secrets). # # This file documents BOTH the Client (per-customer) and Admin (fleet # aggregator) configurations. For a customer stack: leave Application__RunMode # at Client and ignore the Admin section. For the Admin stack: flip # Application__RunMode=Admin, point Database__ConnectionString at the central # fleet DB, and ignore the FleetIngest section. # ── Common ─────────────────────────────────────────────────────────────── # Compose project + container prefix. MUST be lowercase (Docker Compose v2 requirement). # For a customer ID like ABC0001, set the project name to its lowercase form: abc0001. # Containers come out as abc0001_portal / abc0001_grafana / abc0001_timescale. # For the Admin stack a conventional name is `admin`. COMPOSE_PROJECT_NAME=portal-dev # Production-only: public hostname Traefik routes to this stack. # Required by docker-compose.prod.yml. Ignored by the dev compose. CUSTOMER_HOST=abc0001.portal.example.com # RunMode: Client (default, per-customer) or Admin (fleet aggregator). # Single binary, config-selected; see portal/docs/FLEET-DESIGN.md. Application__RunMode=Client # ── Database ───────────────────────────────────────────────────────────── # For Client stacks: each customer has its own POSTGRES_PASSWORD — do NOT reuse # across customers. For the Admin stack, this points at admin_fleet (separate DB). POSTGRES_DB=power_monitoring POSTGRES_USER=power_user POSTGRES_PASSWORD=change_me_for_local_only # ── Portal authentication ──────────────────────────────────────────────── # DefaultAdmin* seeds the first-ever account; ignored once any account with that # email exists. In Production the password MUST be changed or the app refuses # to start. Use `openssl rand -base64 32` to generate. Authentication__DefaultAdminEmail=admin@example.com Authentication__DefaultAdminPassword=ChangeMe123! # ── Grafana ────────────────────────────────────────────────────────────── GRAFANA_ADMIN_PASSWORD=admin # Path prefix Grafana is mounted at behind Traefik. Same-origin embed in the SPA. Grafana__EmbedPathPrefix=/grafana # Prod auth is option (a) Traefik forwardAuth → portal /api/auth/check, wired # via labels in docker-compose.prod.yml. Nothing to set here. # ── White-label (seed values applied only on first boot) ──────────────── # These pre-brand a fresh stack so the customer admin never sees the generic # template on their first sign-in. After the first boot the row in the # customer's DB is authoritative; changing these env vars + restarting does # NOT re-apply them. To restyle later, use Settings → Branding in the UI. # Leave any blank to fall back to the template default (appsettings.template.json). # WhiteLabel__ApplicationName=ACME Power # WhiteLabel__PrimaryColor=#0c4a6e # WhiteLabel__SecondaryColor=#0e7490 # WhiteLabel__AccentColor=#06b6d4 # WhiteLabel__FooterText=© ACME Power 2026 # Logo upload is via Settings → Branding; URL form below is only useful if you # host the logo externally and don't want to upload it. # WhiteLabel__LogoUrl=https://cdn.example.com/acme/logo.png # ── Fleet ingest (Client mode only — push to an Admin stack) ──────────── # Enable per-customer AFTER registering them on the Admin Customers page; the # token is shown once at creation. URL + Token are REQUIRED when Enabled=true # (RunModeGuards refuses startup otherwise). # FleetIngest__Enabled=true # FleetIngest__Url=https://admin.portal.example.com/api/fleet/ingest # FleetIngest__Token= # FleetIngest__IntervalSeconds=60 # FleetIngest__BatchSize=5000 # FleetIngest__BatchMaxBytes=1048576 # ── Admin stack only ───────────────────────────────────────────────────── # When Application__RunMode=Admin: # - Database__ConnectionString MUST be set (no autoprovision for Admin). # - Ignore the FleetIngest__* and WhiteLabel__* sections above unless you # want to brand the Admin operator console differently from customer stacks. # Example: # Application__RunMode=Admin # Database__ConnectionString=Host=timescaledb;Port=5432;Database=admin_fleet;Username=power_user;Password=