Fix silent message rejection when companion RTC diverges from app clock#1889
Open
wbijen wants to merge 1 commit into
Open
Fix silent message rejection when companion RTC diverges from app clock#1889wbijen wants to merge 1 commit into
wbijen wants to merge 1 commit into
Conversation
…diverges from app clock Don't store the login timestamp in last_timestamp. Login uses the companion's RTC while messages use the app's clock - when these diverge, messages get silently rejected by the replay check (sender_timestamp < last_timestamp). Login replay protection is already handled by hasSeen() in the mesh layer. Fixes meshcore-dev#1551
robekl
added a commit
to robekl/MeshCore
that referenced
this pull request
Mar 23, 2026
Reimplements meshcore-dev/MeshCore PR meshcore-dev#1889 on top of 467959c for the 1.14.1 maintenance branch. References: PR: meshcore-dev#1889 Issue: meshcore-dev#1551 Rationale: Login requests use the companion RTC while subsequent messages are validated against the application clock. Persisting the login timestamp into client state can therefore cause silent message rejection when the companion RTC runs ahead. This change stops updating last_timestamp during login and relies on existing mesh-layer replay protection instead. The behavior change is tightly scoped to login handling in three example firmware variants and does not alter packet formats or transport code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix silent message rejection when companion RTC diverges from app clock
Problem
When a companion device's RTC runs ahead of the app's clock, messages sent to Room Servers (and Repeaters/Sensors) are silently rejected. This happens because
last_timestampmixes two different clock sources:ANON_REQ) uses the companion's RTC → e.g.last_timestamp = 1500TXT_MSG) use the app's clock → e.g.sender_timestamp = 1001sender_timestamp >= last_timestampfails:1001 >= 1500→ rejected silentlyThe user sees a successful login but their messages never arrive. No error is shown on either side.
This is the same root cause described in #1551 — the proposed fix there was to change the companion side, but that broke repeater counting for channel messages. This PR fixes it on the server side instead, which avoids touching the companion radio at all.
Solution
Don't store the login timestamp in
last_timestamp. Keeplast_timestamppurely for message/request replay protection (where the clock source is consistent).Login replay protection is already handled by
hasSeen()in the mesh layer, which deduplicates packets by their SHA256 hash. The existing replay check at login time (sender_timestamp <= client->last_timestamp) still works — it just compares against the last message timestamp rather than the last login timestamp.The fix is applied to all three server types:
simple_room_serversimple_repeatersimple_sensorWalkthrough
last_timestamp = 1500last_timestampunchanged (stays 0)1001 >= 1500→ rejected1001 >= 0→ passes1002 >= 1001→ passes1600 > 1500→ passes1600 > 1002→ passesTesting
hasSeen()deduplicationlast_timestampfrom previous messages)