Extend the record to its final shape, revive the dead panels, honor the index
Three firmware waves of the gap-report program, built in parallel
worktrees against the same tip and integrated with both-sides conflict
resolution (two stitch errors — an unopened Doxygen comment and a lost
closing brace — were caught by the compiler, as intended).
Wave 1 — the record's final shape. CredentialRecord gains flags (bit0 =
auto-submit: username, TAB, password, ENTER as one BLE action — and the
trailing ENTER is now conditional where it used to be unconditional),
three typed custom fields (label/value/hidden — hidden values ride
behind the same plaintext-consent header as passwords), a group id, and
TOTP entries gain an account label plus the algorithm byte everywhere.
Groups are a 100-slot encrypted name table (one littlefs block) managed
over GET/POST/DELETE /api/groups — deleting a group only clears labels,
never credentials. The index row carries group, a derived domain column
and real mtime, with names widened to 32 chars; brand is populated at
save time from the url, ending the per-row decrypt the device paid for
logos. URL capacity rises to 192 (the owner's real data maxed at 190),
and every save/import reports per-field truncation instead of silently
clamping. Backup, restore, the normalized importer and the CSV importer
round-trip all of it; the importer maps 1Password vaults and CSV
folder/tags/vault columns into groups server-side.
Wave 2 — six dead routes live: /api/ota/status (the whole Firmware
panel keyed on it), /api/vault/reindex, /api/time/diag (RTC coin-cell
health), the GPS trio (gated to boards that have one), NTP-sync-on-exit
through the AdminMode teardown choke point, and the staged-SD firmware
apply behind the confirm gate. Both full-replace restores now demand
the unlock secret (X-Unlock-Secret) — a backup passphrase proves
nothing about owning the live vault — and the restore body cap is
computed from the device's own backup bound instead of a 64 KB literal.
Wave 3 — the UI stops lying: the audit export honors Accept: text/csv
with real CSV, the web CSV importer honors the type column (Bitwarden
non-logins skipped and counted), LastPass and Proton Pass get real
header signatures, /api/runtime-stats is gone, AutoLockMode appears on
the six boards that hid a working handler, and the device vault lists
honor favorite-first plus manual order with an allocation-free
comparator.
Integration extras: the index envelope buffers and the rekey crypto
scratch now live on the heap (the rekey frame was already past the
32 KB worker stack at the current 200-slot caps — the latent overflow
the dimensioning study flagged); core2_v13's capacity-scaled view-cache
block moves to PSRAM .ext_ram.bss (keys and passwords stay in internal
DRAM), un-breaking its 48-byte link margin; the groups list
distinguishes a corrupt table from an empty one; custom:null is refused
rather than destructive; and the three amalgam suites learned the new
seams, including rebasing an unreachable handler-413 assertion onto the
gate-logic suite that actually pins the bound.
Builds: sticks3_debug, tdeck, cardputer, m5stickc_plus1_debug,
core2_v13_debug (now with SPIRAM bss). Native: full suite green with
nine new suites registered.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r