# Control Service configuration — template. # # Copy to control-service.env and fill in real values. The real file is # gitignored and must never be committed. docker compose loads it with env_file. # # Secrets in this lab also live in ~/.config/environment.d/10-secrets.conf; the # same variable names can be exported there instead. The service reads only the # process environment, so either source works. # Listen address inside the container. CONTROL_ADDR=:8791 # SQLite database on the persistent volume. CONTROL_DB_PATH=/data/control.db CONTROL_APP_ENV=production CONTROL_LOG_LEVEL=info # Timezone weekly schedule windows are evaluated in. Windows are wall-clock, so # this must be the family's zone, not the container's default of UTC. CONTROL_TIMEZONE=Australia/Sydney # Signs the session cookie. Set to a long random string (>= 16 chars). # Generate: openssl rand -hex 32 CONTROL_SESSION_SECRET=change-me-to-a-long-random-string # Session lifetime (Go duration). CONTROL_SESSION_TTL=12h # Secure cookie flag. Keep true behind Caddy/HTTPS. Set false only for # plain-HTTP local testing. CONTROL_COOKIE_SECURE=true # The single dashboard login (Sam). CONTROL_ADMIN_USERNAME=sam # Option A (simple): plaintext password. Constant-time compared at login. CONTROL_ADMIN_PASSWORD=change-me # Option B (preferred): a bcrypt hash; leave CONTROL_ADMIN_PASSWORD empty. # Generate: docker run --rm --entrypoint /app/control webrestrict/control:0.6.0-phase8 hash-password 'your-password' # CONTROL_ADMIN_PASSWORD_HASH=$2a$10$... # --- Admin API (Phase 8) --------------------------------------------------- # The versioned HTTP API the separate Kotlin admin app (kotlin_phone_app_admin) # calls directly. Its bearer token is SEPARATE from the dashboard session login. # Generate a long random value: openssl rand -hex 32 # When empty the API answers 503 "admin API is not configured" and nothing else # changes, so an older deployment keeps working. CONTROL_ADMIN_API_TOKEN=change-me-to-a-long-random-token # The shared device keycode (one code, every device — AGENTS.md §10 rule 3). # Give a bcrypt hash, never the code itself. Generate one with: # docker run --rm --entrypoint /app/control webrestrict/control:0.6.0-phase8 \ # hash-password 'the-code' # The hash is seeded into SQLite at start-up and can be changed later with # `docker exec webrestrict-control /app/control keycode`. # CONTROL_DEVICE_KEYCODE_HASH=$2a$10$... # --- MQTT (Phase 3) -------------------------------------------------------- # The broker is transport only. SQLite stays the source of truth, and only the # Control Service publishes config (AGENTS.md §10 rules 5-6). # Turn the broker link on or off. With it off the dashboard still works and the # config stays in SQLite, unpublished. CONTROL_MQTT_ENABLED=true # The shared Mosquitto on `.13`, seen from inside this container. The compose # file maps host.docker.internal to the host gateway. The TLS listener on 8883 # has been live since 2026-10-13 (deploy/mosquitto/README.md). CONTROL_MQTT_BROKER=tls://host.docker.internal:8883 # The Control Service identity. The broker ACL gives it write access to every # config topic and read access to every status and telemetry topic, nothing else. # Generate the account with deploy/mosquitto/provision-clients.sh, which writes # the values into deploy/mosquitto/clients.env (gitignored). CONTROL_MQTT_CLIENT_ID=control-service CONTROL_MQTT_USERNAME=control-service CONTROL_MQTT_PASSWORD=change-me # PEM bundle used to verify the broker certificate. Required for a tls:// broker. # docker-compose.yml mounts deploy/mosquitto/tls/ here read-only. CONTROL_MQTT_CA_FILE=/etc/webrestrict/tls/ca.crt # Escape hatch for a broker that has no TLS listener: it allows a plaintext, # credential-less link. The service refuses to start without credentials or TLS # unless this is true, and it logs a warning when it is. Leave it false against # the shared Mosquitto (AGENTS.md §10 rule 6). CONTROL_MQTT_INSECURE=false