Add on-device WiFi network add with a masked CharWheel passphrase
Button/keyboard-less boards can now add a WiFi station network entirely
on-device, entering the passphrase through a masked character wheel so the
secret never reaches the screen, the framebuffer, the logs, or serial.
Masking:
- CharWheelProps / CharGridProps gain a `masked` flag; the label line renders
one '*' per entered character instead of the cleartext, while the selection
ring still shows the live dialed glyph so the user can pick characters.
- Both device-adaptive editors (CharWheel on color, CharGrid on mono/e-paper)
gain a `masked` begin() mode: the value buffer is wiped with
InplaceString::secureZeroize on commit, cancel/reset, and idle-sleep, and its
contents are never logged (only the length).
Add flow:
- WifiManagerController grows a synthetic "+ Add network" row; selecting it
hands off through the new SettingsOverlaysHost::onWifiAddNetworkRequested seam.
- SettingsController drives the two stages: SSID via the normal editor, then the
passphrase via the MASKED editor, then VaultStore::addWifiNetwork (SSID 1..32,
PSK empty=open or 8..63, 802.11 bounds enforced by the store). The SSID stash
and the editor buffer are wiped after the encrypted store write.
Testing / QA:
- Native tests assert the masked dot-count equals the char-count and that no
cleartext is ever drawn, on both editors' components.
- New WIFIMGR debug seam opens the real manager for serial-driven QA. On-device
(sticks3) HIL: the masked wheel renders dots, the typed passphrase appears
zero times in the full serial transcript, WIFILIST shows the SSID only, and
the network round-trips through the encrypted store. Capture under
docs/design/qa-captures/sticks3-v2/wifi-psk-masked.png.
Claude-Session: https://claude.ai/code/session_01P6BNTpbgrvnZXSJnNuj8ZJ