Commit Graph
93 Commits
Author SHA1 Message Date
Torlando 28ea218a18 refactor(settings): move status readouts to Status screen, reorganize by use
Settings held live status (GPS fix, storage/RAM/identity) that belongs
on the Status screen, plus a per-second tick() doing SPI flash stat
reads and label churn mid-scroll — the main cause of laggy scrolling.

- GPS section (sats/location/altitude/HDOP/time) -> StatusScreen
- System Info (firmware build, storage, RAM) -> StatusScreen, with
  storage/RAM stat reads throttled to ~5s and stack-buffer snprintfs
  instead of Arduino String concatenation
- Settings gains a Status link row (trackball-reachable) that opens
  Route::STATUS; the per-second SettingsScreen tick/refresh is deleted
- Reordered sections by frequency of use: General (name/brightness/
  timeout/kb-light), Notifications, Network (now includes the
  TCP/Auto/BLE interface switches), Radio (LoRa + params), Delivery,
  Advanced, DANGER: Transport Mode (still final)
- Identity/LXMF hashes shown in Settings were truncated duplicates of
  the Status screen's full display; removed
- main.cpp publishes firmware build + GPS to the Status screen
- Contract test for the storage readout follows the code to StatusScreen
2026-09-04 04:56:07 +00:00
Torlando 403035f115 perf(messages): instant chat open/reopen + deferred full-message view
Uses the microLXMF in-process message-metadata cache (bumped pin
6bea23c -> 58a6eb0, branch feat/conversation-preview-cache):

- Reopening a conversation (and background page fill / paging) is now
  O(1) in-memory once warmed instead of re-reading each message file
  from SPI LittleFS (~230ms/read, ~2.3s per open measured on the T-Deck).
- The cache table is PSRAM-allocated on ESP32; a static .bss placement
  starved internal DRAM and the LVGL task's 8 KiB stack allocation
  failed at boot ("Failed to create LVGL task", hang at startup logo).
- Long-press full-message view now defers to the main loop: the LVGL
  event handler only records the hash; tick_pending_full_message() does
  load_message_content() (uncapped, no msgpack unpack) off the LVGL
  task, then builds the modal. Fixes the capped (600-char) text that
  the in-memory rows carry.
- A peer change cancels an in-flight background fill from the previous
  conversation (it would otherwise prepend the old conversation's
  rows into the new one).

Validated on the T-Deck (T-Deck Plus, 8MB PSRAM): warm opens
reads=3 sync=0-2ms total=15-35ms (was ~2.3s); cold first open per
conversation still pays ~0.6-1.2s disk while warming the cache; no
panics/watchdogs over a ~6-minute interaction session.
Host gates: microLXMF conformance 6/6 (incl. 32-assertion
test_message_metadata_cache), native reference test against pinned
58a6eb0, tdeck build SUCCESS.
2026-09-04 01:30:16 +00:00
Pike 09bf277aa4 chore(messages): remove temporary [PERF] instrumentation
On-device capture confirmed the fix (cold-boot tap: fallbacks=0,
gather=2ms, total=48ms vs 2086ms pre-fix; repeat taps no longer
perpetually fall back). All [PERF] timing markers and stage
variables are removed from refresh()/show()/render_route; the
branch is now clean of diagnostic code.
2026-09-03 17:46:38 +00:00
Pike 8204506b3b perf(messages): persist preview warm-up; cache empty-content tails
On-device [PERF] capture decomposed the remaining ~1s tap latency:
each uncached conversation costs one SPI-LittleFS metadata read
(~230ms). Repeat taps still paid 2 perpetual fallbacks (empty-
content tails never got cached), and the first tap after every
boot paid all 9 (the in-memory repop was never committed to the
index).

- bump microLXMF pin c8d3156 -> 6bea23c (feat/conversation-
  preview-cache): preview_valid index flag so 'cached empty'
  differs from 'unpopulated'; bounded preview copy (the old
  strncpy read past the non-terminated content Bytes); public
  commit_index().
- refresh() re-pops empty-content tails as a valid cached
  preview and arms a one-shot deferred index commit;
  UIManager::update() drains it out-of-lock (flush_pending_
  index_commit, between drops and mark-read) so the warmed
  previews persist and the next cold boot reads them from the
  index.
- fix the [PERF] skip-log total= wrap (printed p_t_diff - p_t0,
  a uint32 underflow; total was already p_t_diff).

[PERF] instrumentation stays in this commit (temporary,
marked); it is removed before merge once the fix is validated
on-device.
2026-09-03 17:12:52 +00:00
Pike af4019f933 diag(messages): [PERF] stage timings for tap-to-visible path
Temporary, removable instrumentation (marked [PERF] throughout):
- refresh(): gather / diff / rebuild stage ms + metadata-fallback count,
  one [PERF] convlist line per call (skip vs build)
- show(): unhide + focus-group ms
- render_route(MESSAGES): whole route window incl. hide_all_screens
All INFO-level so they survive DEBUG-off and land in the serial capture.
2026-09-03 16:02:35 +00:00
Torlando 918f89d63e fix(messages): drop corrupt last message instead of hiding the row
When a conversation's newest message is unreadable, walk the index
newest-to-oldest for the newest readable preview and queue the
unreadable messages for deletion (deferred out of the LVGL lock,
drained by UIManager::update()). The store's delete_message() commits
the index and updates last_message_hash, so the next refresh converges
to the same preview and the row never disappears over one bad message.
2026-09-03 04:14:52 +00:00
Torlando 8ed9551a02 perf(messages): fast conversation-list load + real unread badges
refresh() did heavy unnecessary work per conversation, on the LVGL
render task under the render lock:
  - get_messages_for_conversation() copied the full 256-slot hash array
    (8KB) just to read the newest hash (messages.back());
  - load_message() on that hash read the payload file ~3x, JSON-parsed
    it twice (including the large hex 'packed' blob), hex-decoded and
    msgpack-unpacked the whole message — to extract a 30-char preview
    and a timestamp;
  - the unread badge was rendered from a hardwired 0 even though the
    store maintains and persists unread_count.

Now:
  - newest hash via the new O(1) MessageStore::get_last_message_hash
    (index tail — always hot-tier, no I/O, no array copy);
  - preview/timestamp via load_message_metadata (single open + filtered
    parse, the fast path ChatScreen already uses for the same fields);
  - unread badge from MessageStore::get_conversation_unread_count;
  - badge cleared + mark-read on open (click) and when a message lands
    in the currently-viewed chat, with the LittleFS index commit
    deferred out of the LVGL lock (UIManager::update), matching the
    existing deferred display-name write-through pattern.

Pins microLXMF 59ca70a (PR #10, temporary branch head) for the two new
accessors.
2026-09-03 02:38:03 +00:00
Torlando 40c1869765 fix(lxmf): drop SIGNATURE_INVALID inbound messages
A message whose source identity is KNOWN but whose signature fails to
validate is spoofed or malicious and must not be rendered. The
opportunistic (on_packet) and direct (on_resource_concluded) router
paths already reject these, but the propagated (store-and-forward) path
in process_propagated_lxmf queues them without a signature check, so
UIManager::on_message_received is the single choke point that covers
all three inbound routes.

Drop the message at the top of on_message_received — before the key
request, location ingest, persistence, chat render, and notification
beep — when !signature_validated() && reason == SIGNATURE_INVALID.
SOURCE_UNKNOWN (first contact) is untouched: those still render and
trigger the bounded key request from PR #92. Validated messages are
unaffected.

Add a source-level contract test locking in the drop gate's ordering
relative to every side effect and its enum specificity.
2026-09-02 17:51:52 +00:00
Torlando 97fc8b4562 chore(lxmf): drop temporary location-ingest diagnostic from release path
Greptile P2 on PR #92: the per-inbound-location-frame INFOF logged peer
bytes, precise coordinates, and source-clock skew unconditionally in the
release build. The missing-map-pin investigation is closed (announce
timing, verified physically), so remove the temporary instrumentation
and its ingest_diag_result plumbing, and update the feature comment to
describe the final rate-limit semantics instead of 'remove with the
fix'.
2026-09-02 17:05:23 +00:00
Torlando 2eec34dfe1 test: extract unknown-source key-request policy + host tests
Move the rate-limit/cooldown/cap decision out of the UIManager.cpp
anonymous namespace into a pure, header-only policy
(UI/LXMF/UnknownSourceKeyRequest.h) so it is host-testable without the
ESP/microReticulum stack. UIManager keeps only the side effect
(Transport::request_path).

Adds tests/native/test_unknown_source_key_request.{cpp,py} (24 checks,
ASan+UBSan): new-source request, 5-min cooldown boundary, re-record
resets the window, per-source independence, 64-entry cap with oldest
eviction, and steady-state cost.
2026-09-02 16:22:56 +00:00
Torlando e9fe63e9e4 diag: request network keys for unknown LXMF sources (Sideband request-keys equivalent)
on_message_received now fires a rate-limited RNS path request when an
LXMF message arrives from a SOURCE_UNKNOWN sender, mirroring Sideband's
'Query Network For Keys' button (RNS.Transport.request_path). Any peer
that already knows the source's announce (hub, phone, other node) can
answer with the cached identity, letting the NEXT message from that
peer validate and reach location ingest.

5-minute per-source cooldown, 64-entry cap. App-only diagnostic;
remove with the other ingest diagnostics once the root cause closes.
2026-09-02 16:22:23 +00:00
torlando-agent[bot] 26de7c3e41 fix(nomadnet): keep partial refresh status in chrome 2026-08-18 01:30:14 +00:00
torlando-agent[bot] 1027667f5a feat(nomadnet): integrate dynamic partial rendering 2026-08-17 23:49:13 +00:00
torlando-agent[bot] b6faec8104 feat(nomadnet): add bounded partial core 2026-08-17 19:54:41 +00:00
torlando-agent[bot] ca73ff2e3f fix: show NomadNet navigation progress promptly 2026-08-17 15:47:41 +00:00
torlando-agent[bot] 5b76abfdbe fix: align NomadNet cache response policy 2026-08-17 14:50:00 +00:00
torlando-agent[bot] 2f0f7900ca feat: enforce NomadNet limits and observability 2026-08-17 09:11:10 +00:00
torlando-agent[bot] 16af3b53af feat: add bounded NomadNet SD page cache 2026-08-17 05:48:30 +00:00
torlando-agent[bot] 98dca8e516 feat: add bounded NomadNet forms 2026-08-16 21:36:30 +00:00
torlando-agent[bot] d9945bce24 feat: add bounded NomadNet table rendering 2026-08-16 15:33:52 +00:00
torlando-agent[bot] 9ce2f10107 fix: add NomadNet anchor navigation 2026-08-16 02:40:10 +00:00
torlando-agent[bot] 5961c791d6 Virtualize NomadNet page layout 2026-08-15 02:33:37 +00:00
torlando-agent[bot] b50ae852ac fix(nomadnet): submit configured link variables 2026-08-14 18:11:32 +00:00
torlando-agent[bot] 656f1c03cb fix(nomadnet): validate addresses before teardown 2026-08-14 00:39:04 +00:00
torlando-agent[bot] f605aa386d fix(nomadnet): release directory memory before navigation 2026-08-13 20:58:23 +00:00
torlando-agent[bot] 77e5123455 feat(nomadnet): checkpoint compact page lifecycle
Add bounded compact page rendering, serialized Link and Resource lifecycle handling, TCP reconnect corrections, and deterministic Python RNS conformance coverage.

Physically verified anonymous index retrieval and rendering on T-Deck. Same-destination Link reuse and italic rendering remain follow-up fixes.
2026-08-13 20:58:23 +00:00
torlando-agent[bot] 5a2e4757fa fix: admit outgoing messages before persistence 2026-08-07 19:30:29 +00:00
torlando-agent[bot] 117d3894bd feat: make maps SD-pack only 2026-08-07 05:26:34 +00:00
torlando-agent[bot] faff0c67ba fix: complete map route rebase resolution 2026-08-07 01:39:13 +00:00
torlando-agent[bot] 340c9c240f fix: close map integration safety review 2026-08-07 01:35:35 +00:00
torlando-agent[bot] f55b54eec0 feat: wire safe opt-in map downloads 2026-08-07 01:34:30 +00:00
torlando-agent[bot] f9219426c1 feat: add peer location sharing controls 2026-08-07 01:32:37 +00:00
torlando-agent[bot] c8ba2b5ba3 feat: add bounded offline map screen 2026-08-07 01:31:17 +00:00
torlando-agent[bot] fe0d1f7d55 fix: close location durability and router races 2026-08-07 01:27:49 +00:00
torlando-agent[bot] d7a8f17ac8 feat: persist live location state 2026-08-07 01:27:07 +00:00
torlando-agent[bot] a71e19b65c fix: serialize live router admission 2026-08-07 01:26:45 +00:00
torlando-agent[bot] 0a28266b9c fix: harden live location dispatch boundaries 2026-08-07 01:26:12 +00:00
torlando-agent[bot] 77d68a9d07 feat: route live location telemetry 2026-08-07 01:25:48 +00:00
torlando-agent[bot] 946073ca41 feat: route authenticated location telemetry 2026-08-07 01:23:28 +00:00
torlando-agent[bot] 1c8af97548 fix(nomadnet): bound remote page responses 2026-08-05 18:48:58 +00:00
torlando-agent[bot] 45496a49d4 fix(nomadnet): bound path retry and unblock navigation 2026-08-05 17:34:16 +00:00
torlando-agent[bot] 31c8a08826 fix(ui): single-own LXMF router processing 2026-08-05 16:05:04 +00:00
torlando-agent[bot] 738b7e1c51 fix(nomadnet): preserve successful resource terminal state 2026-08-05 14:08:04 +00:00
torlando-agent[bot] 38839c0229 feat(nomadnet): add scoped discovery and saved navigation 2026-08-05 03:17:00 +00:00
torlando-agent[bot] d0e1ce273c feat(ui): add app launcher and NomadNet MVP 2026-08-05 00:22:59 +00:00
torlando-agent[bot] 7daac8471e fix: make radio activity snapshots task-safe 2026-08-04 16:46:55 +00:00
torlando-agent[bot] 93760af5d5 feat: add current-channel radio activity view 2026-08-04 15:56:56 +00:00
torlando-agent[bot] dcf863e581 [verified] fix: accept LXST profile and mode signals 2026-08-03 01:20:59 +00:00
torlando-agent[bot] 61872480c9 fix: enforce ULBW-only LXST voice profile 2026-08-02 00:39:45 +00:00
torlando-agent[bot] c2c6ae5641 fix(voice): synchronize call termination and TCP announces 2026-08-01 21:32:01 +00:00