fix(reliability): hop governance belongs to rfplan, not boot reconciler (I2)

This commit is contained in:
2026-08-22 16:51:52 -06:00
parent ba3e1b1ae0
commit b25c98b7c7
4 changed files with 71 additions and 7 deletions
@@ -1,10 +1,26 @@
"""Boot-time reconciliation of crash-prone PineAP settings."""
import time
from server import (_apply_uci_wanted, _monitor_down, _raise_monitors,
PINEAPD_SAFE_UCI, device_run)
WANTED_EXTRA = {'pineapd.@pineapd[0].autossidpool': '0'}
POOL_CLEAR_MAX = 20
MONITORS = ('wlan0mon', 'wlan1mon')
# pineapd.wlan1mon.hop is intentionally NOT part of the applied set: channel
# hopping is owned by the RF role manager (_pause_hop/_resume_hop). Hop=1 is
# the healthy recon baseline, so reconciling it here would silently disable a
# pager-enabled setting at every boot and latch GUARD PENDING after any
# attack-role switch.
GR_TTL_SECONDS = 30
_GR_CACHE = {'t': 0.0, 'data': None}
def _wanted():
wanted = {k: v for k, v in PINEAPD_SAFE_UCI.items()
if k != 'pineapd.wlan1mon.hop'}
wanted.update(WANTED_EXTRA)
return wanted
def _pool_size():
@@ -16,7 +32,7 @@ def _pool_size():
def reconcile(clear_pool=True):
changed = _apply_uci_wanted(dict(PINEAPD_SAFE_UCI, **WANTED_EXTRA))
changed = _apply_uci_wanted(_wanted())
pool_cleared = False
if clear_pool and _pool_size() > POOL_CLEAR_MAX:
device_run(['uci', 'delete', 'pineapd.@ssidpool[0].ssid'])
@@ -24,12 +40,20 @@ def reconcile(clear_pool=True):
if changed or pool_cleared:
device_run(['uci', 'commit', 'pineapd'])
raised = _raise_monitors() if any(_monitor_down(m) for m in MONITORS) else []
_GR_CACHE['data'] = None
return {'changed': changed, 'pool_cleared': pool_cleared,
'monitors_raised': raised}
def guard_report():
now = time.time()
cached = _GR_CACHE['data']
if cached is not None and now - _GR_CACHE['t'] < GR_TTL_SECONDS:
return cached
from server import _pending_uci
pending = _pending_uci(dict(PINEAPD_SAFE_UCI, **WANTED_EXTRA))
return {'in_sync': not pending, 'pending': pending,
'pool_size': _pool_size()}
pending = _pending_uci(_wanted())
report = {'in_sync': not pending, 'pending': pending,
'pool_size': _pool_size()}
_GR_CACHE['t'] = now
_GR_CACHE['data'] = report
return report