Ingress and authorization¶
Solar AI Optimizer supports three ways to authenticate dashboard and API access:
- Home Assistant ingress — HA login wraps the app; user identity comes from proxied headers.
- Local admin login — username/password with a signed session cookie for standalone direct access.
- API bearer token —
Authorization: Bearer <API_TOKEN>for scripts and automation.
Ingress always takes priority: when trusted HA user headers are present, the local login page is bypassed.
Deployment patterns¶
A. Standalone with local login¶
Use when exposing the dashboard directly (e.g. http://server:8000).
LOCAL_ADMIN_USERNAME=admin
LOCAL_ADMIN_PASSWORD_HASH=$2b$12$... # bcrypt hash; prefer over plain password
SESSION_SECRET=long-random-string
TRUST_INGRESS_HEADERS=false
Generate a bcrypt hash:
python -c "import bcrypt; print(bcrypt.hashpw(b'your-password', bcrypt.gensalt()).decode())"
The browser shows a login page until POST /api/auth/login succeeds. Sign out from Settings → API security. The login form supports browser password save and autofill (Chrome, Edge, Firefox).
Reset local admin password¶
Credentials can be reset without editing env files manually. The reset script writes to $DATA_DIR/local_auth.env on the data volume; run.sh loads that file on startup, overriding container env for auth keys.
Docker Compose (from repo root):
./scripts/reset-local-password.sh
Proxmox LXC (inside the container host):
bash /opt/solar-ai-optimizer/reset-local-password.sh
# or, after sourcing solar-common.sh:
solar_reset_local_password
Options: --password PASS, --username USER, --keep-sessions, --no-restart.
The script prints the new username and password once and restarts the container by default.
B. Standalone + hass_ingress (Docker)¶
HA users access the app through the HA sidebar; they do not use the local login page. Keep local login for direct :8000 access if the port is published.
Optimizer container:
LOCAL_ADMIN_USERNAME=admin
LOCAL_ADMIN_PASSWORD_HASH=...
SESSION_SECRET=...
TRUST_INGRESS_HEADERS=true
Do not publish port 8000 publicly when using ingress-only access.
When ingress is trusted — native add-on (SUPERVISOR_TOKEN) or TRUST_INGRESS_HEADERS=true — the backend sets X-Frame-Options: SAMEORIGIN so the HA sidebar iframe can embed the panel, and trusts proxied user identity headers. Standalone direct access (neither flag) keeps DENY and does not trust ingress headers.
Home Assistant configuration.yaml:
ingress:
solar_ai:
title: Solar AI
icon: mdi:solar-power-variant
require_admin: false
work_mode: ingress
url: http://solar-ai-optimizer:8000
headers:
X-Remote-User-Id: $user_id
X-Remote-User-Name: $username
X-Remote-User-Display-Name: $user_name
Reload ingress in Developer tools → YAML → INGRESS.
For hass_ingress (HACS), use work_mode: ingress and ui_mode: normal. Do not use ui_mode: replace — that mode is for embedding HA pages, not external apps. hass_ingress v1.3.0+ supports $user_id, $username, and $user_name in the headers block.
C. Home Assistant add-on¶
When SUPERVISOR_TOKEN is set, ingress is trusted automatically (user headers and sidebar iframe framing). Local login is optional and off by default. Open the panel from the HA sidebar.
Roles at a glance
Admin users see all five dashboard tabs (including Assistant and Settings). Viewer users see Overview, Forecast, and History only, with limited overrides on Overview. See the Dashboard user guide → Dashboard roles for screenshots and the full feature matrix.
Admin vs viewer (ingress users)¶
| Role | How determined | Dashboard |
|---|---|---|
| Admin | HA owner or system-admin group; or ADMIN_USER_IDS allowlist |
Full dashboard (Overview, Forecast, History, Assistant, Settings) |
| Viewer | Other HA users via ingress | Overview, Forecast, and History only — limited operator controls on Overview |
Viewers can pause/resume the engine and each subsystem (shedding, grid charge, optimization), toggle grid charge Auto / Force on, and engage the kill switch (with confirmation). They cannot pin reserve SOC, run a control cycle, refresh forecast, clear overrides, toggle shadow/live in the UI, use the Assistant, or open Settings.
Mutating API routes enforce the same limits on the backend. Config and model APIs remain admin-only.
Optional break-glass allowlist:
ADMIN_USER_IDS=abc123,def456
API reference¶
Auth levels:
- Public — no session required (health probes and login bootstrap only).
- Session — requires an authenticated caller (HA ingress identity, local login cookie, or bearer token). Viewers and admins both qualify.
- Admin — requires
is_admin(HA owner/admin group, local admin, bearer token, or open dev mode).
Operational reads (/api/status, forecasts, history, etc.) require Session. Only GET /api/health and the /api/auth/* bootstrap routes are Public.
| Endpoint | Auth | Description |
|---|---|---|
GET /api/health |
Public | Liveness probe |
GET /api/auth/status |
Public | { local_auth_enabled, login_required } |
POST /api/auth/login |
Public | Local admin login; sets cookie |
POST /api/auth/logout |
Public | Clears session cookie |
GET /api/me |
Session | Current user and role |
GET /api/status |
Session | Live dashboard status |
GET /api/forecast |
Session | Forecast chart data |
GET /api/plan |
Session | Latest decision and execution results |
GET /api/grid-stats |
Session | Grid presence statistics |
GET /api/history/telemetry |
Session | Telemetry time series |
GET /api/history/decisions |
Session | Recent decision audit rows |
GET /api/history/executions |
Session | Recent inverter write audit |
GET /api/history/shed-executions |
Session | Recent shed write audit |
GET /api/history/grid-events |
Session | Grid presence events |
GET /api/config/load-shedding |
Session | Read-only load shedding config for viewer dashboard tab |
POST /api/override |
Session | Admin: any override field; viewer: shadow_mode, pause_engine, pause_shedding, pause_grid_charge, pause_optimization, force_grid_charge, force_shed_off, kill_switch (kill_switch requires confirm=true) |
WS /ws |
Session | Live status push (use ?token= when API_TOKEN / MCP_TOKEN is set) |
GET /metrics |
Session | Prometheus metrics (Authorization: Bearer when auth is configured) |
GET /api/config |
Admin | Full dashboard config (viewers denied) |
GET /api/entities |
Admin | HA entity list for Settings autocomplete |
GET /api/shed/device-companions |
Admin | Shed tier companion discovery |
GET /api/shed/snapshots |
Admin | Pending shed snapshots |
POST /api/forecast/refresh |
Admin | Manual forecast refresh |
POST /api/cycle |
Admin | Run control cycle |
POST /api/override/clear |
Admin | Clear all overrides |
PUT /api/config |
Admin | Apply config patch |
POST /api/config/reset |
Admin | Reset config to defaults |
GET /api/model/export |
Admin | Export learned model |
POST /api/model/import |
Admin | Import learned model |
POST /api/model/retrain |
Admin | Retrain ML load model |
POST /api/assistant/ask |
Admin | LLM assistant |
GET /api/debug/trace |
Admin | Decision forensics trace |
POST /api/debug/simulate |
Admin | Dry-run decision simulation |
GET /api/system/update |
Admin | Self-update status |
PATCH /api/system/update/preferences |
Admin | Self-update preferences |
POST /api/system/update |
Admin | Trigger self-update |
POST /api/system/update/restore |
Admin | Restore previous image |
Security checklist¶
- Use
LOCAL_ADMIN_PASSWORD_HASH, not plainLOCAL_ADMIN_PASSWORD, in production. - Set
SESSION_SECRETto a long random value when local auth is enabled. - Set
SESSION_COOKIE_SECURE=truewhen served over HTTPS. - Keep
API_TOKENfor CI/scripts; it grants admin access without the login page. - Block direct LAN access to port
8000when all users should come through HA ingress.
Troubleshooting¶
Owner or admin sees VIEWER badge¶
- Open DevTools → Network and check
GET .../api/me. Expectauth_mode: "ingress"andis_admin: truefor HA owners. - If
is_adminis false, check optimizer logs forconfig/auth/list failedorFailed to fetch HA config/auth/list. - Ensure the HA long-lived token (env
HA_TOKENor Settings → Home Assistant connection) was created from an admin/owner account — the user list API requires admin credentials on the token. - Break-glass: set
ADMIN_USER_IDS=<your-user-id>(copyuser_idfrom/api/me) and restart the container.
Blank iframe or HA UI flashes inside the panel on first load¶
Blank iframe before Solar AI appears: current builds show a branded boot splash (“Verifying access…”) as soon as the ingress iframe loads HTML, before JavaScript runs. Upgrade to v0.5.7+ if you still see a white gap.
HA frontend briefly inside the panel: common with hass_ingress when the ingress URL lacks a trailing slash — relative asset paths resolve to /api/ingress/assets/... instead of /api/ingress/<panel>/assets/..., briefly loading HA's frontend.
- Use
work_mode: ingressandui_mode: normalin your hass_ingress panel config. - In DevTools → Network, confirm JS bundles load from
/api/ingress/<panel>/assets/..., not/api/ingress/assets/.... - Current builds inject a
<base href>and trailing-slash redirect in the dashboard HTML to prevent this; upgrade if you still see the flash on an older image.