# Control plane — jeden plik `.env` **Data:** 2026-07-05 **Status:** wdrożone gpu-fan (agent NVML) i Server UI (panel stacków) używają **jednego** pliku credentials. ## Kanoniczne ścieżki | Środowisko | Plik | |------------|------| | Produkcja (systemd) | `/opt/control-plane/.env` | | Dev w repo | `stacks/control-plane/.env` | | Szablon | `stacks/control-plane/.env.example` | Oba serwisy systemd: ```ini EnvironmentFile=-/opt/control-plane/.env ``` - `server-ui.service` - `gpu-fan.service` ## Zmienne | Zmienna | Opis | |---------|------| | `API_KEY` | **Wspólny** klucz: auth panelu Server UI (`X-API-Key`) + auth agenta gpu-fan (proxy `/api/gpu-fan/*`) | | `SERVER_UI_HOST`, `SERVER_UI_PORT` | Bind Server UI (domyślnie `0.0.0.0:8091`) | | `REPO_ROOT` | Ścieżka do `ubuntu-bare-metal` (wymagane dla Docker Server UI) | | `DOCKER_GID` | GID grupy docker (Docker install) | | `GPU_FAN_AGENT_URL` | URL agenta z perspektywy Server UI (`http://127.0.0.1:18090` native, `http://host.docker.internal:18090` Docker) | | `GPU_FAN_API_HOST`, `GPU_FAN_API_PORT` | Bind agenta gpu-fan (domyślnie `127.0.0.1:18090`) | | `CURVE_PATH`, `POLL_INTERVAL`, `GPU_INDEX` | Konfiguracja NVML / krzywej wentylatorów | **Usunięte:** `GPU_FAN_AGENT_KEY` — duplikat `API_KEY`. ## Ładowanie w Pythonie [`stacks/control-plane/env_loader.py`](../stacks/control-plane/env_loader.py) — `load_control_plane_env(stack_dir)`: **Produkcja** (`stack_dir` pod `/opt/`): tylko `/opt/control-plane/.env` + `os.environ` (systemd). **Dev** (repo `stacks/*`): tylko `stacks/control-plane/.env` + `os.environ`. **Nie ładowane:** `stacks/server-ui/.env`, `/opt/server-ui/.env` (legacy — `API_KEY` usuwany przez `setup-control-plane-env.sh`). Kolejność priorytetu: pliki env → **`os.environ` wygrywa** (systemd `EnvironmentFile`). `api_key_source(stack_dir, values)` — log przy starcie Server UI (bez ujawniania sekretu). Produkcja: `env_loader.py` kopiowany do `/opt/control-plane/env_loader.py` przez `setup-control-plane-env.sh`. ## Instalacja / migracja ```bash sudo bash stacks/server-ui/scripts/setup-control-plane-env.sh sudo systemctl daemon-reload sudo systemctl restart gpu-fan server-ui ``` Skrypt: - tworzy `/opt/control-plane/.env` z example jeśli brak - **migruje** z `/opt/server-ui/.env` i `/opt/gpu-fan/.env` (backup `.env.bak.`) - generuje `API_KEY` jeśli brak lub `change-me` - **sync** `stacks/control-plane/.env` z produkcją (ten sam `API_KEY`) - **usuwa** `API_KEY` / `GPU_FAN_AGENT_KEY` z legacy `stacks/server-ui/.env` - wypisuje gotowy URL z kluczem (`print-api-key-instructions.sh`) Instrukcja dla użytkownika po instalacji: ```bash bash stacks/server-ui/scripts/show-api-key.sh ``` Pełna instalacja: ```bash sudo bash stacks/server-ui/scripts/install-control-plane.sh ``` ## Weryfikacja ```bash bash stacks/server-ui/scripts/show-api-key.sh API_KEY=$(sudo grep ^API_KEY= /opt/control-plane/.env | cut -d= -f2) # Test klucza w panelu curl -s -X POST http://127.0.0.1:8091/api/auth/verify \ -H "Content-Type: application/json" \ -d "{\"api_key\":\"${API_KEY}\"}" # Agent bezpośrednio curl -s http://127.0.0.1:18090/api/status -H "X-API-Key: ${API_KEY}" # Proxy przez Server UI curl -s http://127.0.0.1:8091/api/gpu-fan/health -H "X-API-Key: ${API_KEY}" ``` Oczekiwane: JSON z danymi / `"ok": true`. ## Docker Server UI [`stacks/server-ui/docker-compose.yml`](../stacks/server-ui/docker-compose.yml): ```yaml env_file: - ../control-plane/.env ``` ## Auth flow ```mermaid sequenceDiagram participant Browser participant ServerUI participant GpuFanAgent Browser->>ServerUI: X-API-Key: API_KEY ServerUI->>GpuFanAgent: X-API-Key: API_KEY (ten sam) GpuFanAgent-->>ServerUI: status JSON ServerUI-->>Browser: proxy response ``` ## Poza scope Sekrety workloadów Docker (`LOCALAI_API_KEY`, `HF_TOKEN`, `INITIAL_ADMIN_PASSWORD`, …) pozostają w `stacks//.env` — edycja portów przez Server UI bez zmian. ## Pliki zmienione (implementacja) - `stacks/control-plane/` — `.env.example`, `env_loader.py` - `stacks/server-ui/scripts/setup-control-plane-env.sh` - `server-ui.service`, `gpu-fan.service` - `server-ui/app.py`, `gpu_fan_proxy.py`, `gpu-fan/fan_daemon.py`, `gpu-fan/app.py` - Instalatory: `install-control-plane.sh`, `install.sh`, `install-docker.sh`, `gpu-fan/install.sh` - Skrypty dev: `start.sh`, `deploy-gpu-fan-fix.sh`, `restart-stack.sh`, `enable-lan.sh`, `status.sh` ## Troubleshooting — Invalid API key 1. Klucz tylko z `/opt/control-plane/.env` (`sudo grep ^API_KEY= …`) 2. UI: **Zapisz** → **Sprawdź klucz** 3. Sync: `sudo bash stacks/server-ui/scripts/setup-control-plane-env.sh` 4. Tutorial: [`manual-tutorial/04a-api-key.md`](../manual-tutorial/04a-api-key.md) ## Legacy Stare pliki `/opt/server-ui/.env` i `/opt/gpu-fan/.env` nie są już czytane przez systemd. Po migracji można je usunąć (backup w `.env.bak.*`).