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.
This commit is contained in:
Torlando committed 2026-09-02 16:22:23 +00:00
1 parent ac9c14e8f9
commit e9fe63e9e4
1 file changed
+64
+64
View File
@@ -168,6 +168,62 @@ uint64_t monotonicMillis() {
return static_cast<uint64_t>(esp_timer_get_time() / 1000LL);
}
// ---------------------------------------------------------------------------
// App-only diagnostic: on-demand identity acquisition for unknown LXMF
// sources (mirrors Sideband's "Query Network For Keys" button).
//
// When an LXMF message arrives from a source whose RNS identity has not been
// learned, the router accepts the message but UIManager::on_message_received
// skips location ingest for unauthenticated senders. Nothing in the firmware
// ever triggered the identity learning that microLXMF's comment at
// LXMRouter.cpp:1267 expects ("signature will be validated later if the
// source identity is learned via announce"). This fires a bounded
// RNS path request — the exact mechanism of Sideband's
// RNS.Transport.request_path — so any peer that already knows the source's
// key (the hub, a phone, or another node) can answer with the cached
// announce. The next message from that peer then validates and plots.
//
// Rate-limited per source hash (5 min) and globally (64 entries) to keep
// opportunistic-flood cost to zero in the steady state. Diagnostic for the
// missing-map-pin investigation; remove with the other ingest diagnostics
// once the root cause is closed.
namespace {
struct KeyRequestState {
std::vector<std::pair<std::string, uint64_t>> last_requested;
};
KeyRequestState s_key_request_state;
void request_key_for_unknown_source(const ::LXMF::LXMessage& message) {
if (message.unverified_reason() !=
::LXMF::Type::Message::SOURCE_UNKNOWN) {
return; // Bad signature is not recoverable by asking the network.
}
const RNS::Bytes& source_hash = message.source_hash();
if (source_hash.size() != Telemetry::PEER_ID_SIZE) {
return;
}
const std::string source_hex = source_hash.toHex();
const uint64_t now = monotonicMillis();
const static constexpr uint64_t kKeyRequestIntervalMillis = 5 * 60 * 1000;
for (auto& e : s_key_request_state.last_requested) {
if (e.first == source_hex) {
if (now < e.second + kKeyRequestIntervalMillis) {
return; // Recently requested — wait for the answer to land.
}
e.second = now; // Expired — re-request below.
break;
}
}
if (s_key_request_state.last_requested.size() >= 64U) {
s_key_request_state.last_requested.erase(s_key_request_state.last_requested.begin());
}
s_key_request_state.last_requested.emplace_back(source_hex, now);
INFO(("Requesting network keys for unknown LXMF source " +
source_hex.substr(0, 8) + "...").c_str());
Transport::request_path(source_hash);
}
} // namespace
bool peerIdFromHash(const Bytes& hash, Telemetry::PeerId& output) {
if (hash.size() != Telemetry::PEER_ID_SIZE) return false;
std::memcpy(output.bytes, hash.data(), Telemetry::PEER_ID_SIZE);
@@ -1585,6 +1641,14 @@ void UIManager::on_message_received(::LXMF::LXMessage& message) {
pyxis_test_hook_record_rx(message);
#endif
// Diagnostic: unauthenticated messages are accepted by the router but
// skipped below for location ingest. Ask the network for the source's
// key (Sideband "request keys" equivalent) so the NEXT message from
// this peer can validate and be plotted.
if (!message.signature_validated()) {
request_key_for_unknown_source(message);
}
// Classify authenticated location fields before any conversation write.
// Raw field keys and values are MessagePack spans retained by LXMessage.
Telemetry::InboundLocationMessage inbound{};