Files
Mark-VIII/docs/superpowers/specs/2026-08-19-startup-env-check-design.md
T

99 lines
4.4 KiB
Markdown

# Startup Environment Check + Self-Heal
Date: 2026-08-19
## Problem
Rebooting the Pineapple left Mark VIII in a broken state: the boot-persistent
procd service came back, but nothing reconciled pineapd/UCI/monitors, so the UI
reported state that did not match the device (pool "off" while actually
broadcasting), native recon scans failed, and scans only returned 5GHz results.
Three observed failures:
1. **native recon scan failed**`h_recon_start` proxies
`/api/pineap/recon/new`; it returns 502 when the daemon fails (typically
pineapd down/crash-looping). Nothing verifies pineapd health before the user
starts a scan.
2. **scans only showed 5GHz** — radio0's AP interfaces (wlan0open/wlan0wpa)
pin `wlan0mon` to one 2.4GHz channel (a phy's channel is held by its AP
interface), so 2.4GHz results mostly vanish while 5GHz `wlan1mon` hopping
keeps producing results. The band UCI config itself is correct.
3. **pool "on" while UI shows "off"** — the UI's advertise state is derived
only from UCI `pineapd.@ssidpool[0].disable`. Nothing syncs pineapd's
*runtime* pool broadcast to match UCI. UCI can say disabled while pineapd is
live-broadcasting.
## Design decisions
- Keep boot auto-start; the service self-heals at every startup instead.
- The env check auto-fixes everything fixable, re-verifies, and only fails hard
on core deps (daemon unreachable after fix, pineapd down after restart, recon
DB unreadable).
- 5GHz-only scans are handled by verify + warn (no scan-time AP pausing).
## Implementation
### 1. `env_check()` in server.py (single source of truth)
Runs in order and returns `[{step, ok, detail, action}]` where `ok` is one of
`pass`, `fixed`, `warn`, `fail`:
1. **Daemon reachable**`daemon_sock_call('GET', '/api/pineap/get_config')`.
Report-only.
2. **pineapd alive**`pidof pineapd`. If down: stabilize UCI, restart
`/etc/init.d/pineapd`, re-verify. Core: if still down after restart -> fail.
3. **Sane-off UCI defaults** — reuse the `_stabilize_pineapd` wanted-dict (pool
`disable=1` + clear pool list, `wlan2mon.disable=1`/`hop=0`,
`wlan1mon.bands=5`/`hop=0`, `wlan0mon.bands=2`). Write + commit when
missing. Refactor so the health monitor and env check share the wanted-dict.
4. **Runtime pool sync** — when UCI says pool disabled, call
`_pineap('SSIDPOOL', 'DISABLE')` so pineapd's runtime broadcast matches the
UI. Report the resulting runtime state as `pool_runtime`.
5. **Monitors up**`_bring_monitors_up()` for wlan0mon/wlan1mon.
6. **Recon DB readable** — read-only scan-count query against `RECON_DB`.
Report row count. Core: unreadable -> fail.
7. **2.4GHz sampling** — report enabled radio0 APs (wlan0open/wlan0wpa).
Warn: "2.4GHz under-sampled while an OpenAP/Evil WPA AP is up on radio0".
### 2. CLI mode `server.py --env-check`
`if __name__ == '__main__'` branch: run `env_check()`, print verbose
`[PASS] step — detail` / `[FIXED] ...` / `[WARN] ...` / `[FAIL] ...` lines to
stdout, exit 0 (all pass/warn) or 1 (any core fail).
### 3. Startup self-heal
`serve()` runs `env_check()` before binding, stores the report in module state,
and logs to `/tmp/pagerwebui.log` (covers boot + procd respawn).
### 4. API + UI
- `GET /api/health` gains `env` (last env-check report + timestamp +
pass/fixed/warn/fail counts) and `pool_runtime` (actual runtime broadcast
state after sync).
- `GET /api/recon/status` gains `wlan0_pinned` (true when a radio0 AP is
enabled); the recon scan bar shows a 2.4GHz under-sampling warning pill.
- `h_recon_start` failure includes `detail` in the user-facing error.
### 5. payload.sh
Before starting the service (both foreground and background paths), run
`python3 "$SCRIPT_DIR/server.py" --env-check`, show output verbosely on the
payload screen, and abort with a red message on exit 1. Bump header version to
match the current release.
### 6. Tests
- `tests/test_env_check.py` — fake `device_run` UCI harness (pattern from
`test_health.py`): defaults applied when missing / idempotent when set, pool
list cleared, runtime SSIDPOOL disable invoked when UCI says disabled,
monitors brought up, pineapd restarted when down, DB unreadable -> fail,
CLI `--env-check` prints and exits correctly.
- `test_health.py` updated for the shared stabilization refactor.
## Out of scope
- No scan-time AP pausing / radio0 AP teardown.
- No changes to radio1 / evil-twin / enterprise config handling.