Skip to content

Fix silent message rejection when companion RTC diverges from app clock#1889

Open
wbijen wants to merge 1 commit intomeshcore-dev:devfrom
wbijen:fix/room-server-timestamp-mismatch
Open

Fix silent message rejection when companion RTC diverges from app clock#1889
wbijen wants to merge 1 commit intomeshcore-dev:devfrom
wbijen:fix/room-server-timestamp-mismatch

Conversation

@wbijen
Copy link

@wbijen wbijen commented Mar 1, 2026

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_timestamp mixes two different clock sources:

  1. Login (ANON_REQ) uses the companion's RTC → e.g. last_timestamp = 1500
  2. Messages (TXT_MSG) use the app's clock → e.g. sender_timestamp = 1001
  3. The replay check sender_timestamp >= last_timestamp fails: 1001 >= 1500rejected silently

The 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. Keep last_timestamp purely 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_server
  • simple_repeater
  • simple_sensor

Walkthrough

Step Before (broken) After (fixed)
Login (RTC=1500) last_timestamp = 1500 last_timestamp unchanged (stays 0)
Message (app=1001) 1001 >= 1500rejected 1001 >= 0 → passes
Message (app=1002) never reaches here 1002 >= 1001 → passes
Re-login (RTC=1600) 1600 > 1500 → passes 1600 > 1002 → passes

Testing

  • Login from companion with RTC ahead of app clock → messages should no longer be silently dropped
  • Login replay protection still works via mesh-layer hasSeen() deduplication
  • Message replay protection is unaffected (still uses last_timestamp from previous messages)

…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant