The settings routes returned the WiFi passphrase, MQTT password and the
login password and PIN in plain text, and the debug log printed them and
the API key. Responses now carry hasPassphrase/hasPassword/hasPin flags,
logs mask the values, and an omitted secret keeps the stored one.
The UI loads secret fields empty and treats a blank field as "keep" while
the SSID or username is unchanged. Clearing a secret without changing the
SSID or username is listed in docs/KNOWN_ISSUES.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
loop() and the async web server task shared one global Preferences. A
begin() from one task failed while the other had a namespace open, and the
clear() or remove() after it then hit the other task's namespace: saving the
radio settings while a command committed its rolling code could erase every
rolling code.
Each function now opens NVS with a local Preferences. NVS serializes access
internally and the destructor closes the handle, so no lock is added (see
docs/adr/0001). No new tests: nothing here builds natively, and deleting the
global makes any missed use a compile error.
The read-then-commit races on rolling codes across tasks remain and are
tracked in docs/KNOWN_ISSUES.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>