Finish the admin portal surface and give destruction one bar everywhere
The six endpoint groups the portal rendered nothing for are now firmware,
one module per file, each behind the auth policy its usage demands
(polled reads are SessionReadOnly so a dashboard cannot hold the radio up):
- admin_system_api: /api/system{,/partitions,/hardware}, selftest, beep,
reboot and sleep through the FSM flag seam. Hardware rows carry PROBED
chip identities published from Core 1 through atomics — a build flag
selects a code path, it does not name a part (the StickS3 PMIC lesson).
- admin_totp_api: live one-time codes gated on clock trust before any
vault I/O, answering the whole validity window (remaining_s) so the
page polls once per period instead of once per second.
- admin_credentials_api: the list gains user/favorite/order/hasTotp, and
every delete — single or batch — is prepare (unlock secret) → commit
(one-shot nonce), executed as ONE meta commit and ONE index rewrite by
VaultStore::deleteCredentialBatch. The bare DELETE route is gone.
- admin_ble_api: device name + per-bond rename (POST, not PATCH — the
HTTP facade deliberately stays GET/POST/DELETE), with a bounded
name-store flush so a rename survives a power cycle.
- SD restore wizard: stage → gated commit (unlock secret + imported PIN +
X-Confirm-Restore) → idempotent abort, with foreign work factors
clamped to [1000, 4x local] and session teardown on any failure past
the point of no return. The ungated one-shot import route is removed.
- admin_telemetry_api: on-demand battery sampler with a columnar chunked
body, ownership-guarded so a portal claim can never stop a
debug-console capture, drained on the UI core with a bounded loop.
Destruction now has one bar everywhere: credentials, TOTP entries (new),
SD keyfile/format, wipe, factory reset and the restore commit all demand
proof of the unlock secret through admin_confirm.h. BLE bonds stay the
deliberate exception — re-pairing is recoverable.
The device side gains the owner-facing half of the pinned admin network:
a "Fixed admin network" toggle (OptionId::AdminNetworkFixed, key
ap_fixed) that arms a pin request; the password is still drawn only in
generateApPassword(), before esp_wifi_start(), where the bootloader
entropy source is sound — the toggle records intent, the draw stays in
its one proven-safe place.
The SPA follows every contract (columnar telemetry, the restore 401-with-
restored:true success screen, per-id delete outcomes), the mock mirrors
them, and portal_checks gains seven second-wave HIL checks including
"the retired routes stay retired" and "secretless destruction is refused
before any KDF".
The batch pays its own DRAM bill: the fleet build caught core2_v13_debug
overflowing dram0 by 344 bytes (classic-ESP32 debug links with a few
hundred bytes of headroom). Three trims inside the new modules return
~405 bytes of permanent .bss: the telemetry channel registry is sized to
what the fleet registers (8 slots, not 24), the staged delete batch is
heap-allocated on first use, and the device-information observations
(power snapshot, chip probes, storage figures) live in one heap block
allocated on first touch — they are session observations, not boot
state. The 32 KB vault state and 12.5 KB app context stay deliberately
static: key material must not reach PSRAM, and their fixed reservation
is the linker's honest canary.
Native: 2705/2705. Builds: full 43-env fleet (the two native test envs
that pio run cannot build alone are the known Unity flake), then
core2_v13_debug, sticks3_debug, m5stickc_plus1_debug and core2_v13
re-verified after the DRAM trims. Guards and pre-commit hooks all green.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r